From ebb27a5852e04cbae9caf12b4327d891be939b41 Mon Sep 17 00:00:00 2001 From: Jerry Date: Mon, 30 Jan 2023 15:23:11 +0800 Subject: [PATCH] update docs, and some related comments and generated codes --- docs/.vuepress/config.js | 16 +- docs/README.md | 8 +- docs/advanced/README.md | 5 + docs/advanced/custom-start.md | 206 ++++++++++++++++++ docs/advanced/multi-process.md | 98 +++++++++ docs/components/common/global-defines.md | 2 +- docs/components/common/hot-update.md | 35 +++ docs/components/store/file-system.md | 2 +- docs/guide/configuration.md | 7 +- docs/guide/debugging.md | 12 +- docs/guide/{get_started.md => get-started.md} | 0 docs/guide/installation.md | 2 +- docs/update/v3.md | 28 +-- .../Command/Generate/TextGenerateCommand.php | 31 ++- src/ZM/Command/Server/ServerStartCommand.php | 10 +- src/ZM/Context/Trait/HttpTrait.php | 3 - src/ZM/Framework.php | 1 + 17 files changed, 422 insertions(+), 44 deletions(-) create mode 100644 docs/advanced/README.md create mode 100644 docs/advanced/custom-start.md create mode 100644 docs/advanced/multi-process.md create mode 100644 docs/components/common/hot-update.md rename docs/guide/{get_started.md => get-started.md} (100%) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 1eab185b..5e5244e7 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -28,6 +28,7 @@ module.exports = { { text: '指南', link: '/guide/' }, { text: '事件', link: '/event/' }, { text: '组件', link: '/components/bot/bot-context' }, + { text: '进阶', link: '/advanced/' }, { text: '更新日志', link: '/update/v3' }, { text: 'API 文档', link: '/doxy/', target: '_blank' }, { text: '炸毛框架 v2', link: 'https://docs-v2.zhamao.xin/' } @@ -43,7 +44,7 @@ module.exports = { 'installation', 'configuration', 'structure', - 'get_started', + 'get-started', 'debugging', ] } @@ -91,6 +92,7 @@ module.exports = { 'common/class-alias', 'common/global-defines', 'common/logging', + 'common/hot-update', ] }, { @@ -104,6 +106,18 @@ module.exports = { ] } ], + '/advanced/': [ + { + title: '进阶', + collapsable: false, + sidebarDepth: 1, + children: [ + '', + 'multi-process', + 'custom-start', + ] + } + ], '/update/': [ { title: '更新日志', diff --git a/docs/README.md b/docs/README.md index 61e27484..55f0e4a9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,13 +13,13 @@ actions: size: large features: - title: 高性能 - details: 基于 PHP 的 Swoole 高性能扩展,利用 WebSocket 进行与 OneBot 协议兼容的聊天机器人软件的通信,还有数据库连接池、内存缓存、多任务进程等特色,大幅增强性能。 + details: 使用基于 PHP 的高性能扩展库,利用 OneBot 协议实现与聊天机器人软件的通信,还有数据库连接池、内存缓存、多任务进程等特色,大幅增强性能。 - title: 易于开发 - details: 所有功能采用模块化设计,除特殊情况外几乎所有功能都不需要修改框架内任意代码,框架采用灵活的注解进行各类事件绑定,同时支持下断点调试。 + details: 所有功能采用插件化设计,除特殊情况外几乎所有功能都不需要修改框架内任意代码,框架自带插件的安装、打包、解包功能,方便分发和管理。 - title: 接口直观 details: 支持命令、普通文本、正则匹配、自然语言处理等多种对话解析方式,利用协程巧妙实现了直观的交互式会话模式,同时支持多种富文本的处理。 footer: | - Apache-2.0 Licensed  |  Copyright © 2019-2023 Zhamao Developer Team  |  沪ICP备2021010446号-1 + Apache-2.0 Licensed  |  Copyright © 2019-2023 CrazyBot Team  |  沪ICP备2021010446号-1 --- # 快速上手 @@ -35,7 +35,7 @@ bash <(curl -fsSL https://zhamao.xin/v3.sh) ## 运行框架 ```bash -cd zhamao-app/ +cd zhamao-v3/ ./zhamao server ``` diff --git a/docs/advanced/README.md b/docs/advanced/README.md new file mode 100644 index 00000000..a80f9895 --- /dev/null +++ b/docs/advanced/README.md @@ -0,0 +1,5 @@ +# 进阶 + +在本章,下面的部分将详细说明一些具体的案例和自定义框架的操作。 + +> 更多进阶教程敬请期待....(或者你可以选择提 Issue 到框架 GitHub,有需求就写入文档) diff --git a/docs/advanced/custom-start.md b/docs/advanced/custom-start.md new file mode 100644 index 00000000..3c3fa460 --- /dev/null +++ b/docs/advanced/custom-start.md @@ -0,0 +1,206 @@ +# 框架高级启动 + +## 框架安装方式 + +框架提供了多种安装和运行的方式。 + +- Composer 库引入模式 +- 源码模式 +- Phar Composer 库引入模式 +- Phar 源码模式 +- 单文件模式 + +### Composer 库引入模式 + +框架在最初的指南教程中,给出的安装方式是 Composer 库引入模式,这种模式安装框架也是比较灵活的。 +Composer 库引入模式,顾名思义就是把框架本体当作一个 Composer 库来引入,这样就可以通过 Composer 来管理框架的版本,例如使用 `composer update` 更新。 + +Composer 库引入模式的文件夹结构如下,我们假设你的项目目录为 `/root/zhamao-v3`: + +``` +/root/zhamao-v3/ +├── composer.json // 引入框架和项目的 composer.json 描述文件 +├── composer.lock // Composer 生成的 lock 文件 +├── config/ // 炸毛框架依赖的全局配置文件 +│ ├── global.php // 全局配置文件,大部分配置项在这里配置 +│ ├── container.php // 框架的容器配置文件 +│ └── motd.txt // 框架启动时的 MOTD 提示语 +├── plugins/ // 开发者编写和安装的插件目录 +│ ├── example/ // 插件示例,这里以名称为 example 的插件为例 +│ │ ├── main.php // 这个插件使用了单文件格式,main.php 为插件的核心代码 +│ │ └── zmplugin.json // 这个文件描述了炸毛框架插件的元信息,包括插件名称、版本、作者等 +├── vendor/ // 你自身项目的依赖库,由 Composer 生成 +│ ├── zhamao/ +│ │ └── framework/ // 框架的本体源码根目录 +│ │ ├── src/ZM/ // 框架的核心代码 +│ │ └── ...... // 框架的根目录其他文件 +│ └── ....... // 其他依赖库 +├── zhamao // 启动炸毛框架的入口文件 +└── zm_data/ // 存放框架依赖的持久化数据目录,如日志、缓存等 +``` + +### 源码模式 + +源码模式不是指开发者的源码,而是框架的源码。也就是说,源码模式是从 GitHub 直接拉取框架源码仓库后使用框架的模式。 +源码模式允许你对框架本身进行一系列修改,框架本体就可以直接运行。例如,你可以在框架的源码中添加一些自己的代码,或者修改框架的某些功能。 + +源码模式的结构和 Composer 库引入模式有些许不同,因为框架本身就是一个项目,所以它的目录结构也是一个项目的目录结构。 + +``` +/root/zhamao-framework/ +├── composer.json // 框架本身的 composer.json +├── composer.lock // Composer 生成的 lock 文件 +├── config/ // 炸毛框架依赖的全局配置文件 +│ ├── global.php // 全局配置文件,大部分配置项在这里配置 +│ ├── container.php // 框架的容器配置文件 +│ └── motd.txt // 框架启动时的 MOTD 提示语 +├── plugins/ // 开发者编写和安装的插件目录 +│ ├── example/ // 插件示例,这里以名称为 example 的插件为例 +│ │ ├── main.php // 这个插件使用了单文件格式,main.php 为插件的核心代码 +│ │ └── zmplugin.json // 这个文件描述了炸毛框架插件的元信息,包括插件名称、版本、作者等 +├── src/ // 框架的本体源码根目录 +│ ├── ZM/ // 框架的核心代码 +│ └── ...... // 框架的根目录其他文件 +├── vendor/ // 框架本身依赖的 Composer 库文件夹 +│ └── ....... // 其他依赖库 +├── zhamao // 启动炸毛框架的入口文件 +└── zm_data/ // 存放框架依赖的持久化数据目录,如日志、缓存等 +``` + +源码模式下你可以在 `src/` 目录编写你的项目或修改框架源码运行,此时在 `src/` 下的代码虽然在设置 psr-4 自动加载后会被框架解析,但在该目录下的代码不属于插件的范畴。 +如果你不喜欢在插件的形式下开发自己的功能,也可以直接在 src 目录下编写代码。这种方式除了源码模式外,Composer 库引入模式下也可以在你的目录新建一个 `src/` 文件夹并设置自动加载, +以实现在非插件环境下加载你的项目。 + +### Phar 模式 + +Phar 模式的意思是将框架和依赖的相关 Composer 库打包为一个可直接使用的 Phar 文件,框架必需的依赖(除 PECL 扩展外)均被包含在一个文件内,方便框架本体分发。 + +Phar 模式主要面向发布到生产环境和减少小文件,但使用 Phar 模式不便于依赖的更新,所有依赖的库将锁定在打包时的版本。 + +Phar 模式也分两个小种类,Composer 库引入模式和源码模式。如果你不关注框架本体目录,仅开发功能,无论使用插件形式还是 `src/` 形式,那么在使用上这两种方式没有区别。 + +框架在未来发布版本时,会同时发布一个打包好的 Phar 版本,你可以直接下载使用。 + +在使用 Phar 时,你的目录结构可能为这样: + +``` +/root/your-zhamao-app/ +├── plugins/ // 开发者编写和安装的插件目录 +│ ├── example/ // 插件示例,这里以名称为 example 的插件为例 +│ │ ├── main.php // 这个插件使用了单文件格式,main.php 为插件的核心代码 +│ │ └── zmplugin.json // 这个文件描述了炸毛框架插件的元信息,包括插件名称、版本、作者等 +├── config/ // 配置文件目录 +├── zhamao.phar // 炸毛框架本体的 Phar +├── zm_data/ // 存放框架依赖的持久化数据目录,如日志、缓存等 +``` + +### 单文件模式 + +单文件模式和 Phar 模式几乎一样,单文件模式为一个单独的二进制文件,这个二进制文件使用 phpmicro 项目的打包功能将 php-cli 和炸毛框架的 Phar 合成为一个文件, +即可直接使用。这种方式的好处是不需要额外的 php-cli 环境,但是文件体积会比 Phar 模式大一些。 + +## 框架启动参数 + +框架启动时可以传入一些参数,这些命令行参数是用于框架启动时的配置。 + +> 这里框架启动参数指的是 `./zhamao server` 启动框架的参数,而不是 `./zhamao` 命令的参数。有关 `./zhamao` 命令的其他功能,可以参考 [组件 - 命令行](/components/common/cli.html)。 + +### --config-dir + +指定配置文件目录,如果不指定,框架会使用默认的配置文件目录。 + +```bash +./zhamao server --config-dir=/path/to/your/config/dir +``` + +### --driver + +指定框架使用的驱动,目前支持 `swoole` 和 `workerman` 两种驱动,如果不指定,框架会采用 `config/global.php` 配置文件内的驱动。 + +```bash +./zhamao server --driver=swoole +``` + +### --log-level + +指定框架组件 zhamao/logger 显示日志的等级。logger 组件符合 psr-3 日志接口,支持设置 8 个等级: + +`emergency`、`alert`、`critical`、`error`、`warning`、`notice`、`info`、`debug`。 + +::: warning 注意 + +如果你想采用其他 psr-3 日志组件,此配置无效。 + +::: + +```bash +./zhamao server --log-level=debug +``` + +### --daemon + +以守护进程模式启动框架。此参数将直接在输出 motd 后将进程挂到 init 下运行,后台常驻。 + +> 单进程模式下,此参数无效。 + +```bash +./zhamao server --daemon # 执行后,你可以退出当前终端而不退出框架 +``` + +### --worker-num + +指定框架启动的 worker 进程数。未指定时默认采用 `config/global.php` 下对应驱动的配置(默认为 1)。 + +```bash +./zhamao server --worker-num=8 +``` + +::: warning 注意 + +- 在启动多 worker 时,需要注意无法使用 LightCache,必须切换为 KVRedis 等支持跨进程的组件。 +- Windows 环境不支持设置进程数。 + +::: + +### --watch + +启动框架的热更新,并启用调试模式。 + +> 此功能暂未完成,敬请期待。 + +### --env + +设置环境类型 (production, development, staging)。 + +如果不设置此参数,框架默认使用 development 环境类型。 + +```bash +./zhamao server --env=production +``` + +### --disable-safe-exit + +禁用安全退出。如果不设置此参数,框架会在收到 SIGINT 信号时,等待所有请求处理完毕后再退出。 +设置此参数后,使用键盘 Ctrl+C 会立刻停止所有进程,不会执行退出框架的正常流程,例如保存 LightCache 持久化数据等。 + +```bash +./zhamao server --disable-safe-exit +``` + +### --no-state-check + +取消框架在启动前的重复启动检查。如果不设置此参数,框架会在启动前检查是否有其他进程正在运行,如果有则会退出。 +设置此参数后,框架会忽略重复启动检查,可能会导致多个框架进程同时运行。 + +```bash +./zhamao server --no-state-check +``` + +### --private-mode + +启动时隐藏框架的配置信息和 MOTD,避免打印到终端。配合 logger 组件的选项可以达到启动时除紧急日志外没有任何输出内容到终端。 + +```bash +./zhamao server --private-mode +./zhamao server --private-mode --log-level=emergency +``` diff --git a/docs/advanced/multi-process.md b/docs/advanced/multi-process.md new file mode 100644 index 00000000..66b19143 --- /dev/null +++ b/docs/advanced/multi-process.md @@ -0,0 +1,98 @@ +# 框架的多进程 + +首先对于多进程概念,对于传统 PHP 程序员可能比较陌生,唯一接触到的地方可能就是 php-fpm 等一些方式处理时间长的请求时开进程去执行。关于多进程,我觉得廖雪峰的 Python 多进程这段讲的不错: + +> Unix/Linux 操作系统提供了一个`fork()`系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是`fork()`调用一次, +> 返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。 + +这里面的重点在于,多进程的创建,是父进程的复制,然后两个进程接下来运行的代码和存的内容就分道扬镳了。 + +PHP 也是如此,框架的多进程又是怎么一回事呢?为什么要采用多进程呢? + +## 作用 + +使用过框架的你一定知道,框架是以命令行方式运行 PHP 的,而命令行方式运行 PHP,就代表要常驻内存,就像 Python、Node.js 一样。 +而默认情况下,比如 Python 的 Flask 为单线程单进程模式,也就是说同时只能处理一个 Web 请求。 +但大部分情况下,比如 Node.js,提供的都是异步 I/O,这也就是说明它在 Web 处理请求上,可同时承接的 I/O 密集型请求会更多一些, +这样在对一般的 Web 应用中 I/O 密集型场景非常有用,而且往往只需要单进程也可以承载上万的并发请求。 + +在炸毛框架中,因为框架基于 Swoole、Workerman 等驱动构建,在使用 Swoole 驱动时可以将一部分 I/O 操作协程化。 +协程就是针对 I/O 操作进行一个调度,类似异步的 Node.js,所以针对项目中存在太多的 SQL 语句执行、文件读写的话,只需换成 Swoole 驱动,无需做任何修改,也可以达到很好的性能。 + +**但是**,CPU 密集型的应用或 Workerman 怎么办呢?假设我的 Web 应用有大量的排序、md5 运算怎么办呢? +这样的阻塞,假设是一个超级大的 for 循环或者是要执行很长时间的 while 循环,CPU 一直在被占用。多进程就是针对 CPU 密集型的应用说 yes 的一个方案。 + +## 多进程类型 + +框架多进程中,所有进程的功能和名称是有区别的。 + +- Master 进程:主进程,负责执行最初的启动代码,也是接下来其他类型进程的父进程,它一般不会执行任何业务代码,只是负责管理其他进程。 +- Manager 进程:在使用 Swoole 驱动且使用了 `SWOOLE_PROCESS` 模式启动框架后,会出现,由 Master 进程 fork 而来,用于管理 Worker 进程。 +- Worker 进程:主要逻辑的工作进程,用户态代码在这里被加载。在 Swoole 驱动的 `SWOOLE_PROCESS` 模式下由 Manager 进程 fork 而来,在 Workerman 驱动下由 Master 进程 fork 而来。 +- TaskWorker 进程:在使用 Swoole 驱动且设置了 `taskworker_num` 时,由 Master 或 Manager 进程 fork 而来,用于处理耗时的任务。 +- User 进程:在指定了 UserProcessStartEvent 事件下,驱动抽象层会调用驱动的进程创建方法,创建一个用户自定义的子进程,由 Master 或 Manager 进程 fork 而来。 + +## 框架可用的进程模式 + +首先,如果按照“指南”章节中的安装和配置使用框架,则框架默认的进程为单 Worker 模式。 + +现在框架支持的多进程模式有以下几种(`n > 1`): + +1. `MST1#1`:Workerman 的单 Worker 模式,也是框架默认启动的模式。 +2. `MST1#n`:Workerman 的多 Worker 模式,由 Master 进程 fork 出多个 Worker 进程,`n` 为 Worker 进程数。 +3. `MST1#0`:Workerman 的无 Worker 模式(在 Windows 上使用的默认模式),用户态代码在 Master 进程中执行,此时 Master 进程也是 Worker #0 进程。 +4. `MST1MAN1#1`:Swoole 的 `SWOOLE_PROCESS` 启动模式下的单 Worker 模式,如果切换驱动为 Swoole 时,此模式为框架默认的启动模式。 +5. `MST1MAN1#n`:Swoole 的 `SWOOLE_PROCESS` 启动模式下的多 Worker 模式,由 Manager 进程 fork 出多个 Worker 进程,`n` 为 Worker 进程数。 +6. `MST1MAN0#0`:Swoole 的 `SWOOLE_BASE` 启动模式下的无 Worker 模式,用户态代码在 Master 进程中执行,此时 Master 进程也是 Worker #0 进程。 +7. `MST1MAN0#1`:Swoole 的 `SWOOLE_BASE` 启动模式下的单 Worker 模式,如果切换驱动为 Swoole 时,此模式下仅有 Master、Worker #0 两个进程存在。 +8. `MST1MAN0#n`:Swoole 的 `SWOOLE_BASE` 启动模式下的多 Worker 模式,类似于 `MST1#n`,由 Master 进程 fork 出多个 Worker 进程,`n` 为 Worker 进程数。 + +::: tip 提示 + +- 在 Windows 环境(MSVC 环境的 PHP),框架目前只能使用 Workerman 驱动并使用 `MST1#0` 模式。 +- 在 Linux、macOS 环境,使用 Workerman 驱动时,由于 Workerman 自身的限制,无法使用 `MST1#0` 模式。 + +::: + +### 框架为什么使用单 Worker 模式 + +炸毛框架从最初的炸毛机器人、炸毛框架 0.x、1.x、2.x 到现在的 v3 版本,一直在探索最合适的进程模式。 + +炸毛机器人本体使用了单 Worker 模式,原因:便于热更新(重载 reload 功能),机器人的逻辑代码在 Worker 进程中执行,重载时只需要重启 Worker 进程即可。 + +框架的项目还在叫 cqbot-swoole 时,采用的是无 Worker 模式,重载应用不是很方便。 +框架 1.x 延续了现在炸毛机器人本体的进程模式,但 2.x 发生了变化。框架 2.x 默认使用 Swoole 作为底层驱动且默认使用多 Worker 模式启动。 +主要原因是想充分利用 Swoole 的特性以及提升框架的性能上限。 +但在 2.x 的开发者调研情况来看,使用多进程在开发层面带来的不便远远大于性能上的提升,因此框架 3.x 继续默认使用单 Worker 模式启动。 + +但我们总不能在新版本对特性做出退步,总有需要多 Worker 或单进程(即 `MST1#0` 和 `MST1MAN0#0`)的时候。 +所以在框架 3.0 全新的大版本中,我们对多进程本身也加入了支持,但是默认仍然是单 Worker。 + +> 单 Worker 不是单进程,单 Worker 是至少有两个进程,一个 Master、一个 Worker。如果是 Swoole,还可能有 Manager。 + +- 单进程:适合任意环境。 +- 单 Worker:适合生产环境和开发环境,同时也便于重载。 +- 多进程:适合生产环境。 + +在使用单进程模式时,调试代码变得十分容易,比如使用 psysh 下断点将是非常稳定可靠的,因为只有一个进程在运行。 +单 Worker 模式做到了用户态代码与主进程隔离,方便重载,同时也有一定的便捷性,比如可以在 Worker 进程使用全局变量和静态成员变量等。 + +## 多进程的内存隔离 + +多进程模式下有内存隔离,而且各个进程的父子关系也很明确。进程是程序在操作系统中的一个边界,和自己有关的一切变量、内容和代码都在自己的进程内。 +不同进程之间如果不使用管道等方式,是不可以互相访问的。而加上开始描述的,创建子进程是一个复制自身的过程,所以也就会有如下图的情况: + +![多进程-内存隔离](https://img.zhamao.xin/framework/multi-process-variable.png) + +我们以静态类为例,设置一个进程中的全局变量。这里就会出现,同一个静态变量在多个进程中完全不同的值的结果。 +此后,我们将会在 Worker 进程中执行用户的代码。 +如果设置 Worker 数量仅为 1 的话,那么就简单许多了,你还是可以使用全局变量或静态类来存储你想要的内容而不用担心这种多个进程变量隔离的情况, +因为用户的 Web 请求处理的代码只会在一个 Worker 进程中执行。 +如果像设置了多个 Worker,则收到的机器人事件或 HTTP 请求等就有可能出现在不同的 Worker 进程中,给全局变量设值就一定会造成不同步的问题。 +这时我们就不可以使用全局变量做数据同步(注意,我说的是数据同步)。 + +如果想实现跨进程通信,也有很多种方案,有几种方案是炸毛推荐的: + +- 使用 Redis、SQL 等数据库。例如使用 Redis 后,你可以把需要的数据写入 Redis,再方便地通过框架地 KV 接口无损从 LightCache 切换以实现跨进程通信。 +- 使用 Swoole 驱动并在 Setup 阶段设置 Atomic、Swoole Table 等共享内存的组件,方便跨进程通信。 +- 使用 Swoole 的 PipeMessage 也可以直接方便地在多个 Worker 之间相互通信,但目前 [php-libonebot](https://github.com/botuniverse/php-libonebot) 暂无支持的计划。 diff --git a/docs/components/common/global-defines.md b/docs/components/common/global-defines.md index 375e85ed..de36e17e 100644 --- a/docs/components/common/global-defines.md +++ b/docs/components/common/global-defines.md @@ -375,4 +375,4 @@ public function testRoute(HttpRequestEvent $event) 获取一个 KV 库实例。 -有关 KV 库的使用,见 [组件 - KV 库(TODO)]()。 +有关 KV 库的使用,见 [组件 - KV 缓存](/components/store/cache)。 diff --git a/docs/components/common/hot-update.md b/docs/components/common/hot-update.md new file mode 100644 index 00000000..0b59bb8a --- /dev/null +++ b/docs/components/common/hot-update.md @@ -0,0 +1,35 @@ +# 框架调试 - 热更新和重载 + +::: danger + +目前此功能还在开发中,暂不可用,预计 3.0 正式版发布后可使用。 + +::: + +框架使用了 Workerman、Swoole、Choir 等驱动作为底层协议和进程管理模型,在使用了 Worker 进程模式启动框架后,你可以使用 Worker 进程的重载功能来更新你的代码。 + +首先,如果你不了解框架的进程结构,请先了解 [进阶开发 - 框架的多进程](/advanced/multi-process)。 + +开发者在使用框架开发相应的插件时,经常需要重新载入插件的代码。热更新和重载就是为了在不停止主进程的情况下在 Worker 进程内重新加载代码,以达到代码更新的作用。 +你在插件目录开发的插件代码,一般为 `plugins/xxx/` 目录下的代码,均可使用重载功能实现热更新。 + +::: warning 注意 + +- Linux、macOS 环境使用 Workerman、Swoole 驱动默认配置情况均可使用重载。 +- Swoole 驱动模式下使用 SWOOLE_BASE 模式,且未设置 Worker 数量时不可使用重载。 +- Workerman 驱动模式下除 Windows 外均可使用重载。 + +::: + +使用重载的方式很简单,在另一个终端内进入框架的工作目录,并执行命令: + +```bash +./zhamao server:reload +``` + +或者你也可以在代码中调用 `\ZM\Framework::getInstance()->reload()` 进行重载。 + +如果你不想手动调用重载命令或代码,你也可以在启动框架时使用 `--watch` 参数来监听 plugins 目录文件变化。 + +在使用 `--watch` 启动热更新功能后,框架将每 3 秒比较一次文件变化(不包含插件内的 vendor 第三方库目录)。涉及更新到 `.php` 文件的,将会自动重载一次。 +重载后,所有插件都会按照正常启动流程执行一次,例如执行 `@Init` 注解等。 diff --git a/docs/components/store/file-system.md b/docs/components/store/file-system.md index 27bec09e..f14e63bb 100644 --- a/docs/components/store/file-system.md +++ b/docs/components/store/file-system.md @@ -114,7 +114,7 @@ $result = \ZM\Store\FileSystem::getClassesPsr4('src/TestApp', 'TestApp\\'); 无论在使用 `FileSystem` 类上方列举提供的扩充功能,还是 PHP 原生的读写文件方式,在框架内涉及目录的位置尽量使用绝对路径。 -框架提供了一系列目录常量,可以配合目录常量来构成绝对路径。有关常量的定义,见 [留个空TODO]()。 +框架提供了一系列目录常量,可以配合目录常量来构成绝对路径。有关常量的定义,见 [常量列表](/components/common/global-defines)。 ```php $file = file_get_contents(WORKING_DIR . '/composer.json'); diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 62e1c0bb..8921aed8 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -32,7 +32,7 @@ $config['servers'] = [ `driver` 配置项用于指定框架的底层驱动,目前支持以下驱动: -- `swoole`:基于 Swoole,需要安装 `swoole` 扩展 +- `swoole`:基于 Swoole,需要安装 `swoole` 扩展,协程支持很好 - `workerman`:基于 Workerman,无需安装额外扩展,但在 PHP 8.0 下无法使用协程 - `choir`:基于 Choir,尚未完成,不建议使用 @@ -43,12 +43,11 @@ $config['servers'] = [ - `http`:基础的 HTTP 服务(监听传入的 HTTP 请求) - `host`:监听地址 - `port`:监听端口 - - `flags`: + - `flags`:一个 int 值,用于在事件中获取源于哪一个 HTTP 监听的地址和端口 - `websocket`:WebSocket 服务(允许其他客户端接入) - `host`:监听地址 - `port`:监听端口 -- `websocket_reverse`:反向 WebSocket 服务(框架主动发起连接) - - `url`:连接地址 + - `flags`:同上方 HTTP 的 `flags` 以上所有服务器均支持 `access_token` 配置项,用于指定[鉴权方式](/components/bot/authorization.md)。 diff --git a/docs/guide/debugging.md b/docs/guide/debugging.md index 63bc6e01..17d95f4e 100644 --- a/docs/guide/debugging.md +++ b/docs/guide/debugging.md @@ -18,7 +18,17 @@ dump($var); 根据运行环境的不同(Swoole、Workerman 等),你可以使用不同的调试工具。 -例如,你可以使用 [Xdebug](https://xdebug.org/) 或 [yasd](https://github.com/swoole/yasd) 等。 +例如,你可以使用 [Xdebug](https://xdebug.org/) 或 [yasd](https://github.com/swoole/yasd) 等。这类工具的使用方式可参照对应文档。 + +或者,你也可以在代码中使用 psysh 下断点并查看变量。但在使用 psysh 时要注意,在多进程模式(多 Worker、含用户进程、TaskWorker 等模式)下,会出现未定义的行为,最好在单进程模式下使用。 + +```php +$x = 123; +// ... 你的代码 +eval(\Psy\sh()); // 下断点位置 +// ... 你的代码 +$y = 456; +``` ## 热更新 diff --git a/docs/guide/get_started.md b/docs/guide/get-started.md similarity index 100% rename from docs/guide/get_started.md rename to docs/guide/get-started.md diff --git a/docs/guide/installation.md b/docs/guide/installation.md index f7249255..411d3fdf 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -101,4 +101,4 @@ cd zhamao-v3 ## 更多的环境部署和开发方式 -除了上述方式之外,框架还支持源码模式、守护进程等运行方式,详情请参阅 [进阶开发]。 +除了上述方式之外,框架还支持源码模式、守护进程等运行方式,详情请参阅 [进阶开发](/advanced/)。 diff --git a/docs/update/v3.md b/docs/update/v3.md index 977e7705..8e041cee 100644 --- a/docs/update/v3.md +++ b/docs/update/v3.md @@ -1,12 +1,12 @@ # 更新日志 -> 本页面由框架自动生成 +> 本页面由框架命令 `./zhamao generate:text update-log-md` 自动生成 ## v3.0.0-beta6 > 更新时间:2023-01-15 -* 文档更新 - 机器人上下文 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#243](https://github.com/zhamao-robot/zhamao-framework/pull/243) +* 本次更新包含文档更新内容 5 个 * 更新版权所有年份 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#244](https://github.com/zhamao-robot/zhamao-framework/pull/244) * 修复 Doxygen 缺少权限.. by [@sunxyw](https://github.com/sunxyw) in [PR#245](https://github.com/zhamao-robot/zhamao-framework/pull/245) * 更新部分文档和新增有关生成器 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#246](https://github.com/zhamao-robot/zhamao-framework/pull/246) @@ -19,8 +19,7 @@ * 添加环境变量支持 by [@sunxyw](https://github.com/sunxyw) in [PR#255](https://github.com/zhamao-robot/zhamao-framework/pull/255) * 重构插件加载器、BotContext、AnnotationMap 新增方法、修复部分 Bug by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#254](https://github.com/zhamao-robot/zhamao-framework/pull/254) - -**Full Changelog**: +**源码变更记录**: ## v3.0.0-beta5 @@ -44,15 +43,14 @@ * 重构新的 waitMessage 到 prompt by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#241](https://github.com/zhamao-robot/zhamao-framework/pull/241) * 添加 OneBot 12 文件上传和下载工具 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#242](https://github.com/zhamao-robot/zhamao-framework/pull/242) - -**Full Changelog**: +**源码变更记录**: ## v3.0.0-beta4 > 更新时间:2022-12-31 * 切换 Git Hook 至 Captainhook by [@sunxyw](https://github.com/sunxyw) in [PR#218](https://github.com/zhamao-robot/zhamao-framework/pull/218) -* 升级 v3 文档构建发布路径 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#219](https://github.com/zhamao-robot/zhamao-framework/pull/219) +* 本次更新包含文档更新内容 2 个 * 根据最新的分支命名更新 Workflows by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#220](https://github.com/zhamao-robot/zhamao-framework/pull/220) * 更新部分文档 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#221](https://github.com/zhamao-robot/zhamao-framework/pull/221) * 将 KV 库接口调整为 PSR-16 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#222](https://github.com/zhamao-robot/zhamao-framework/pull/222) @@ -60,8 +58,7 @@ * 边缘 Bug 修复 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#224](https://github.com/zhamao-robot/zhamao-framework/pull/224) * 重构配置类配置 by [@sunxyw](https://github.com/sunxyw) in [PR#225](https://github.com/zhamao-robot/zhamao-framework/pull/225) - -**Full Changelog**: +**源码变更记录**: ## v3.0.0-beta3 @@ -70,7 +67,7 @@ * 添加 Windows 入口 by [@sunxyw](https://github.com/sunxyw) in [PR#208](https://github.com/zhamao-robot/zhamao-framework/pull/208) * 重构异常处理 by [@sunxyw](https://github.com/sunxyw) in [PR#209](https://github.com/zhamao-robot/zhamao-framework/pull/209) * 添加命令帮助插件 by [@sunxyw](https://github.com/sunxyw) in [PR#210](https://github.com/zhamao-robot/zhamao-framework/pull/210) -* 加(一些)事件文档 by [@sunxyw](https://github.com/sunxyw) in [PR#212](https://github.com/zhamao-robot/zhamao-framework/pull/212) +* 本次更新包含文档更新内容 1 个 * 修复 BotContext 不同步问题 by [@sunxyw](https://github.com/sunxyw) in [PR#211](https://github.com/zhamao-robot/zhamao-framework/pull/211) * 让容器支持协程 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#213](https://github.com/zhamao-robot/zhamao-framework/pull/213) * 拆分 Bot 动作到 Trait 以及更新一些类型强化的代码 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#214](https://github.com/zhamao-robot/zhamao-framework/pull/214) @@ -78,8 +75,7 @@ * 新增 ZMRequest by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#216](https://github.com/zhamao-robot/zhamao-framework/pull/216) * 新增全新的 LightCache by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#217](https://github.com/zhamao-robot/zhamao-framework/pull/217) - -**Full Changelog**: +**源码变更记录**: ## v3.0.0-beta2 @@ -97,11 +93,10 @@ * 添加 HttpEventListener 测试 by [@sunxyw](https://github.com/sunxyw) in [PR#200](https://github.com/zhamao-robot/zhamao-framework/pull/200) * 添加插件生成功能 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#202](https://github.com/zhamao-robot/zhamao-framework/pull/202) * 完善 BotCommand 和 CommandArgument 的解析 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#203](https://github.com/zhamao-robot/zhamao-framework/pull/203) -* 重写文档 by [@sunxyw](https://github.com/sunxyw) in [PR#205](https://github.com/zhamao-robot/zhamao-framework/pull/205) +* 本次更新包含文档更新内容 1 个 * 发布 beta2 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#206](https://github.com/zhamao-robot/zhamao-framework/pull/206) - -**Full Changelog**: +**源码变更记录**: ## v3.0.0-beta1 @@ -155,6 +150,5 @@ * 容器增加 class_alias 支持 by [@sunxyw](https://github.com/sunxyw) in [PR#189](https://github.com/zhamao-robot/zhamao-framework/pull/189) * Beta 1 发布 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#188](https://github.com/zhamao-robot/zhamao-framework/pull/188) - -**Full Changelog**: +**源码变更记录**: diff --git a/src/ZM/Command/Generate/TextGenerateCommand.php b/src/ZM/Command/Generate/TextGenerateCommand.php index 51c9295e..a31ee883 100644 --- a/src/ZM/Command/Generate/TextGenerateCommand.php +++ b/src/ZM/Command/Generate/TextGenerateCommand.php @@ -62,15 +62,35 @@ class TextGenerateCommand extends Command return static::FAILURE; } $json = json_decode($api, true); - $line = '# 更新日志' . "\r\n\r\n> 本页面由框架自动生成\r\n\r\n"; + $line = '# 更新日志' . "\r\n\r\n> 本页面由框架命令 `./zhamao generate:text update-log-md` 自动生成\r\n\r\n"; foreach ($json as $v) { $version = $v['tag_name']; if (str_starts_with($version, '2.')) { continue; } + $doc_count = 0; $time = '> 更新时间:' . date('Y-m-d', strtotime($v['published_at'])); - $line .= '## v' . $v['tag_name'] . "\r\n\r\n" . $time . "\r\n\r\n" . trim(str_replace("## What's Changed", '', $v['body'])) . "\r\n\r\n"; + $line .= '## v' . $v['tag_name'] . "\r\n\r\n" . $time . "\r\n\r\n"; + $v['body'] = trim(str_replace("## What's Changed", '', $v['body'])); + $bodies = explode("\r\n", $v['body']); + foreach ($bodies as $ks => $vs) { + if (str_contains($vs, '文档')) { + ++$doc_count; + if ($doc_count === 1) { + $bodies[$ks] = '* 本次更新包含文档更新内容 {cnt} 个'; + } + } + } + $v['body'] = implode("\r\n", $bodies); + if ($doc_count > 0) { + $v['body'] = str_replace('{cnt}', strval($doc_count), $v['body']); + } + $line .= $v['body'] . "\r\n\r\n"; } + // 将双空行转换为单空行 + $line = str_replace("\r\n\r\n\r\n", "\r\n\r\n", $line); + + // 转换文本换行符格式为 LF $line = str_replace("\r\n", "\n", $line); // 将所有的链接转换为可点击的链接,例如 https://example.com -> @@ -82,6 +102,13 @@ class TextGenerateCommand extends Command // 将 mention 转换为可点击的链接,例如 @sunxyw -> [@sunxyw](https://github.com/sunxyw) $line = preg_replace('/(?<=^|\s)@([\w.]+)(? 0) { + $line = str_replace('{cnt}', strval($doc_count), $line); + } + file_put_contents(FRAMEWORK_ROOT_DIR . '/docs/update/v3.md', $line); return static::SUCCESS; } diff --git a/src/ZM/Command/Server/ServerStartCommand.php b/src/ZM/Command/Server/ServerStartCommand.php index 9bcb5acb..09b554ed 100644 --- a/src/ZM/Command/Server/ServerStartCommand.php +++ b/src/ZM/Command/Server/ServerStartCommand.php @@ -32,7 +32,7 @@ class ServerStartCommand extends ServerCommand new InputOption('log-level', null, InputOption::VALUE_REQUIRED, '调整消息等级到debug (log-level=4)'), new InputOption('daemon', null, null, '以守护进程的方式运行框架'), new InputOption('worker-num', null, InputOption::VALUE_REQUIRED, '启动框架时运行的 Worker 进程数量'), - new InputOption('watch', null, null, '监听 src/ 目录的文件变化并热更新'), + new InputOption('watch', null, null, '监听 plugins/ 目录下各个插件的文件变化并热更新(还不能用)'), new InputOption('env', null, InputOption::VALUE_REQUIRED, '设置环境类型 (production, development, staging)'), new InputOption('disable-safe-exit', null, null, '关闭安全退出(关闭后按CtrlC时直接杀死进程)'), new InputOption('no-state-check', null, null, '关闭启动前框架运行状态检查'), @@ -44,19 +44,11 @@ class ServerStartCommand extends ServerCommand /** * @throws ZMKnownException - * @throws InitException * @throws \Exception * @noinspection PhpComposerExtensionStubsInspection */ protected function execute(InputInterface $input, OutputInterface $output): int { - // 这段用于config的环境解析,但显然不是很好的方式,应该改成一个独立的方法,不应该在这里检查,但暂时搁置,TODO - /* if (($opt = $input->getOption('env')) !== null) { - if (!in_array($opt, ['production', 'staging', 'development', ''])) { - $output->writeln(' "--env" option only accept production, development, staging and [empty] ! '); - return 1; - } - }*/ // 如果是支持多进程模式的,那么就检查框架进程的状态 if (ProcessManager::isSupportedMultiProcess()) { $state = ProcessStateManager::getProcessState(ZM_PROCESS_MASTER); diff --git a/src/ZM/Context/Trait/HttpTrait.php b/src/ZM/Context/Trait/HttpTrait.php index d2d778c3..ca4b4df0 100644 --- a/src/ZM/Context/Trait/HttpTrait.php +++ b/src/ZM/Context/Trait/HttpTrait.php @@ -31,9 +31,6 @@ trait HttpTrait return $obj; } - /** - * {@inheritDoc} - */ public function withResponse(ResponseInterface $response) { $this->getHttpRequestEvent()->withResponse($response); diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index a398439b..1f06a985 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -226,6 +226,7 @@ class Framework $this->printMotd(); } + // 注册添加容器依赖的事件 ContainerBindingListener::listenForEvents(); // 添加框架需要监听的顶层事件监听器