mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-03 06:45:36 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbe4addd83 | ||
|
|
600829645d | ||
|
|
68280cfe7e | ||
|
|
c5523aa95d | ||
|
|
93a68a5582 | ||
|
|
6155236d3c | ||
| 28f7f20728 | |||
| 235256d679 |
@@ -1,3 +0,0 @@
|
||||
FROM zmbot/swoole:latest
|
||||
|
||||
# TODO: auto-setup entrypoint
|
||||
14
SECURITY.md
14
SECURITY.md
@@ -1,14 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.0 | :white_check_mark: |
|
||||
| 1.6.x | :white_check_mark: |
|
||||
| 1.1.x | :x: |
|
||||
| 1.0.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a bug which is safety related, you should post a new issue named **Security Issue**, and I will check it as soon as possible.
|
||||
31
bin/systemd
31
bin/systemd
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php /** @since 1.2 */
|
||||
switch ($argv[1] ?? '') {
|
||||
case '--generate':
|
||||
case '':
|
||||
generate($argv);
|
||||
break;
|
||||
case '--help':
|
||||
case '-h':
|
||||
default:
|
||||
echo "\nUsage: " . $argv[0] . " [OPTION]\n";
|
||||
echo "\nzhamao-framework systemd generator.";
|
||||
echo "\n\n -h, --help\t\tShow this help menu";
|
||||
echo "\n --generate\tGenerate a systemd service file\n\n";
|
||||
break;
|
||||
}
|
||||
|
||||
function generate($argv) {
|
||||
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
|
||||
$s .= "\nUser=" . exec("whoami");
|
||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||
$s .= "\nWorkingDirectory=" . getcwd();
|
||||
if ($argv[0] == "systemd" && !file_exists(getcwd() . '/systemd'))
|
||||
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server";
|
||||
else
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server";
|
||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||
@mkdir(getcwd() . "/resources/");
|
||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||
echo "File successfully generated. Path: " . getcwd() . "/resources/zhamao.service\n";
|
||||
}
|
||||
@@ -29,10 +29,10 @@
|
||||
"symfony/polyfill-mbstring": "^1.20",
|
||||
"symfony/console": "^5.1",
|
||||
"symfony/routing": "^5.1",
|
||||
"zhamao/connection-manager": "*@dev",
|
||||
"zhamao/console": "^1.0",
|
||||
"zhamao/console": "^1.0.10",
|
||||
"zhamao/config": "^1.0",
|
||||
"zhamao/request": "*@dev"
|
||||
"zhamao/request": "^1.1",
|
||||
"zhamao/connection-manager": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "*",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
/** @noinspection PhpFullyQualifiedNameUsageInspection */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
global $config;
|
||||
|
||||
/** bind host */
|
||||
$config['host'] = '0.0.0.0';
|
||||
@@ -39,7 +38,7 @@ $config['light_cache'] = [
|
||||
'size' => 512, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, //单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, //Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => $config['zm_data'].'_cache.json',
|
||||
'persistence_path' => $config['zm_data'] . '_cache.json',
|
||||
'auto_save_interval' => 900
|
||||
];
|
||||
|
||||
@@ -112,15 +111,15 @@ $config['server_event_handler_class'] = [
|
||||
// 这里添加例如 \ZM\Event\ServerEventHandler::class 这样的启动注解类
|
||||
];
|
||||
|
||||
/** 服务器启用的外部第三方和内部插件 */
|
||||
$config['modules']['onebot'] = [
|
||||
// 机器人解析模块,关闭后无法使用如@CQCommand等注解
|
||||
/** 机器人解析模块,关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
|
||||
$config['onebot'] = [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false
|
||||
'single_bot_mode' => false,
|
||||
'message_level' => 99999
|
||||
];
|
||||
|
||||
$config['modules']['remote_terminal'] = [
|
||||
// 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接)
|
||||
/** 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
||||
$config['remote_terminal'] = [
|
||||
'status' => false,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 20002,
|
||||
|
||||
22
docs/FAQ.md
22
docs/FAQ.md
@@ -1,3 +1,23 @@
|
||||
# FAQ
|
||||
|
||||
这里会写一些常见的疑难解答。
|
||||
这里会写一些常见的疑难解答。
|
||||
|
||||
## 启动时报错 Address already in use
|
||||
|
||||
1. 检查是否开启了两次框架,每个端口只能开启一个框架。
|
||||
2. 如果是之前已经在 20001 端口或者你设置了别的应用同样占用此端口,更换配置文件 `global.php` 中的 port 即可。
|
||||
3. 如果是之前框架成功启动,但是使用 Ctrl+C 停止后再次启动导致的报错,请根据下面的步骤来检查是否存在僵尸进程。
|
||||
|
||||
- 如果系统内装有 `htop`,可以直接在 `htop` 中开启 Tree 模式并使用 filter 过滤 php,检查残留的框架进程。
|
||||
- 如果系统没有 `htop`,使用 `ps aux | grep vendor/bin/start | grep -v grep` 如果存在进程,请使用以下命令尝试杀掉:
|
||||
|
||||
```bash
|
||||
# 如果确定框架的数据都已保存且没有需要保存的缓存数据,直接杀掉 SIGKILL 即可,输入下面这条
|
||||
ps aux | grep vendor/bin/start | grep -v grep | awk '{print $2}' | xargs kill -9
|
||||
|
||||
# 如果不确定框架是不是还继续运行,想尝试正常关闭(走一遍储存保存数据的事件),使用下面这条
|
||||
# 首先使用 'ps aux | grep vendor/bin/start | grep -v grep' 找到进程中第二列最小的pid
|
||||
# 然后使用下面的这条命令,假设最小的pid是23643
|
||||
kill -INT 23643
|
||||
# 如果使用 ps aux 看不到框架相关进程,证明关闭成功,否则需要使用第一条强行杀死
|
||||
```
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 0 B |
@@ -271,6 +271,8 @@ zm_dump($pass);
|
||||
|
||||
## zm_config()
|
||||
|
||||
> v2.4.0 起可用。
|
||||
|
||||
同 `ZMConfig::get()`。
|
||||
|
||||
定义:`zm_config($name, $key = null)`。
|
||||
@@ -284,6 +286,8 @@ zm_config("global", "swoole"); //等同于 ZMConfig::get("global", "swoole");
|
||||
|
||||
## zm_info()
|
||||
|
||||
> v2.4.0 起可用。(下面的 log 类也一样)
|
||||
|
||||
同 `Console::info($msg)`。
|
||||
|
||||
## zm_debug()
|
||||
|
||||
25
docs/update/config.md
Normal file
25
docs/update/config.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# 配置文件变更记录
|
||||
|
||||
这里将会记录各个主版本的框架升级后,涉及 `global.php` 的更新日志,你可以根据这里描述的内容与你的旧配置文件进行合并。
|
||||
|
||||
## v2.4.0 (build 400)
|
||||
- 调整 `$config['modules']['onebot']` 配置项到 `$config['onebot']`,旧版本的此段会向下兼容,建议更新,
|
||||
- 新增 `$config['remote_terminal']` 远程终端的配置项,新增此段即可。
|
||||
|
||||
更新部分:
|
||||
```php
|
||||
/** 机器人解析模块,关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
|
||||
$config['onebot'] = [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false,
|
||||
'message_level' => 99999
|
||||
];
|
||||
|
||||
/** 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
||||
$config['remote_terminal'] = [
|
||||
'status' => false,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 20002,
|
||||
'token' => ''
|
||||
];
|
||||
```
|
||||
@@ -1,23 +1,53 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## v2.3.4 (build 397)
|
||||
## v2.4.1 (build 401)
|
||||
|
||||
> 更新时间:2021.3.25
|
||||
|
||||
- 修复:开启框架时导致的报错
|
||||
|
||||
## v2.4.0(build 400)
|
||||
|
||||
> 更新时间:2021.3.25
|
||||
|
||||
- 新增:检查全局配置文件的命令
|
||||
- 新增:全局配置文件更新记录
|
||||
- 依赖变更:**Swoole 最低版本需要 4.5.0**
|
||||
- 优化:reload 和 stop 命令重载和停止框架的逻辑
|
||||
- 新增:`$_running_annotation` 变量,可在注解事件中的类使用
|
||||
- 新增:远程终端(Remote Terminal),弥补原来删掉的本地终端,通过 nc 命令连接即可
|
||||
- 新增:启动参数 `--worker-num`,`--task-worker-num`,`--remote-terminal`
|
||||
- 更新:全局配置文件结构
|
||||
- 新增:Swoole 计时器报错处理
|
||||
- 新增:全局方法(`zm_dump()`,`zm_error()`,`zm_warning()`,`zm_info()`,`zm_success()`,`zm_verbose()`,`zm_debug()`,`zm_config()`)
|
||||
- 新增:示例模块的图灵机器人和 at 机器人的处理函数
|
||||
- 新增:MessageUtil 工具类新增 `isAtMe(), splitCommand(), matchCommand()` 方法
|
||||
- 新增:ProcessManager 进程管理类新增 `workerAction(), sendActionToWorker(), resumeAllWorkerCoroutines()` 方法
|
||||
- 优化:CQCommand 的匹配逻辑
|
||||
- 新增:支持添加自定义远程终端指令的 `@TerminalCommand` 注解
|
||||
- 新增:图灵机器人 API 封装函数
|
||||
- 新增:ZMUtil 工具杂项类 `getReloadableFiles()` 函数
|
||||
- 新增:`vendor/bin/start systemd:generate` 生成 systemd 配置文件的功能
|
||||
- 新增:`vendor/bin/start check:config` 检查配置文件更新的命令
|
||||
- 新增:`vendor/bin/init` 新增 `--force` 参数,覆盖现有文件重新生成
|
||||
- 新增:MessageUtil 新增方法:`addShortCommand()`,用于快速添加静态文本问答回复的
|
||||
|
||||
以下是需要**手动更新**或**更换新写法**的部分:
|
||||
|
||||
- 配置文件 `global.php` 中的 `modules` 字段展开,内置模块的配置一律平铺到外面。详见 [更新日志 - 配置文件变更](/update/config)。
|
||||
|
||||
以下是默认机器人直接连接产生的变更:
|
||||
|
||||
- 2.4.0 新增了默认回复其他人 at 的消息,如果不需要,请将 `Hello.php` 中的 `changeAt()` 和 `turingAPI()` 方法删除。
|
||||
|
||||
## v2.3.5 (build 398)
|
||||
|
||||
> 更新时间:2021.3.23
|
||||
|
||||
- 修复:版本号显示
|
||||
- 修复:MySQL 数据库查询导致的一系列问题
|
||||
- 修复:内存泄露问题
|
||||
|
||||
## v2.3.3 (build 396)
|
||||
|
||||
> 更新时间:2021.3.23
|
||||
|
||||
- 修复:Composer 调试时出现加载重复的问题
|
||||
|
||||
## v2.3.2 (build 395)
|
||||
|
||||
> 更新时间:2021.3.23
|
||||
|
||||
- 修复:数据库查询部分情况无法正常使用的 bug
|
||||
- 修复:内存泄漏问题
|
||||
> 2.3.2-2.3.4 版本由于操作失误导致代码不完整,请直接使用 2.3.5 即可。
|
||||
|
||||
## v2.3.1
|
||||
|
||||
@@ -25,7 +55,6 @@
|
||||
|
||||
- 规范代码,修复一个小报错的 bug
|
||||
|
||||
|
||||
## v2.3.0
|
||||
|
||||
> 更新时间:2021.3.16
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 385 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 115 KiB |
@@ -2,14 +2,12 @@
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\Command\TerminalCommand;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQMessage;
|
||||
use ZM\Annotation\Http\Middleware;
|
||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
use ZM\Annotation\Swoole\OnStart;
|
||||
use ZM\API\CQ;
|
||||
use ZM\API\TuringAPI;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
@@ -18,6 +16,7 @@ use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Exception\InterruptException;
|
||||
use ZM\Module\QQBot;
|
||||
use ZM\Requests\ZMRequest;
|
||||
use ZM\Utils\MessageUtil;
|
||||
use ZM\Utils\ZMUtil;
|
||||
@@ -29,12 +28,6 @@ use ZM\Utils\ZMUtil;
|
||||
*/
|
||||
class Hello
|
||||
{
|
||||
/**
|
||||
* @OnStart()
|
||||
*/
|
||||
public function onStart() {
|
||||
}
|
||||
|
||||
/*
|
||||
* 默认的图片监听路由对应目录,如需要使用可取消下面的注释,把上面的 /* 换成 /**
|
||||
* @OnStart(-1)
|
||||
@@ -81,29 +74,37 @@ class Hello
|
||||
}
|
||||
|
||||
/**
|
||||
* 图灵机器人的内置实现,在tuling123.com申请一个apikey填入下方变量即可。
|
||||
* @CQCommand(start_with="机器人",end_with="机器人",message_type="group")
|
||||
* @CQMessage(message_type="private",level=1)
|
||||
*/
|
||||
public function turingAPI() {
|
||||
$user_id = ctx()->getUserId();
|
||||
$api = "83513e3d316f44de9c952cda4c9aed30"; // 请在这里填入你的图灵机器人的apikey
|
||||
$api = ""; // 请在这里填入你的图灵机器人的apikey
|
||||
if ($api === "") return false; //如果没有填入apikey则此功能关闭
|
||||
if (($this->_running_annotation ?? null) instanceof CQCommand) {
|
||||
$msg = ctx()->getFullArg("我在!有什么事吗?");
|
||||
} else {
|
||||
$msg = ctx()->getMessage();
|
||||
}
|
||||
zm_dump($msg);
|
||||
return TuringAPI::getTuringMsg($msg, $user_id, $api);
|
||||
ctx()->setMessage($msg);
|
||||
if (MessageUtil::matchCommand($msg, ctx()->getData())->status === false) {
|
||||
return TuringAPI::getTuringMsg($msg, $user_id, $api);
|
||||
} else {
|
||||
QQBot::getInstance()->handle(ctx()->getData(), ctx()->getCache("level") + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应at机器人的消息
|
||||
* @CQBefore("message")
|
||||
*/
|
||||
public function changeAt() {
|
||||
if (MessageUtil::isAtMe(ctx()->getMessage(), ctx()->getRobotId())) {
|
||||
$msg = str_replace(CQ::at(ctx()->getRobotId()), "", ctx()->getMessage());
|
||||
ctx()->setMessage("机器人 ".$msg);
|
||||
ctx()->setMessage("机器人" . $msg);
|
||||
Console::info(ctx()->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
61
src/ZM/Command/CheckConfigCommand.php
Normal file
61
src/ZM/Command/CheckConfigCommand.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class CheckConfigCommand extends Command
|
||||
{
|
||||
protected static $defaultName = 'check:config';
|
||||
|
||||
private $need_update = false;
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("检查配置文件是否和框架当前版本有更新");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
if (LOAD_MODE !== 1) {
|
||||
$output->writeln("<error>仅限在Composer依赖模式中使用此命令!");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$current_cfg = getcwd() . "/config/";
|
||||
$remote_cfg = include_once WORKING_DIR . "/config/global.php";
|
||||
if (file_exists($current_cfg . "global.php")) {
|
||||
$this->check($remote_cfg, "global.php", $output);
|
||||
}
|
||||
if (file_exists($current_cfg . "global.development.php")) {
|
||||
$this->check($remote_cfg, "global.development.php", $output);
|
||||
}
|
||||
if (file_exists($current_cfg . "global.staging.php")) {
|
||||
$this->check($remote_cfg, "global.staging.php", $output);
|
||||
}
|
||||
if (file_exists($current_cfg . "global.production.php")) {
|
||||
$this->check($remote_cfg, "global.production.php", $output);
|
||||
}
|
||||
if ($this->need_update === true) {
|
||||
$output->writeln("<comment>有配置文件需要更新,详情见文档 https://framework.zhamao.xin/update/config.md</comment>");
|
||||
} else {
|
||||
$output->writeln("<info>配置文件暂无更新!</info>");
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @noinspection PhpIncludeInspection
|
||||
*/
|
||||
private function check($remote, $local, OutputInterface $out) {
|
||||
$local_file = include_once getcwd() . "/config/".$local;
|
||||
foreach($remote as $k => $v) {
|
||||
$out->writeln("<comment>正在检查".$k."</comment>");
|
||||
if (!isset($local_file[$k])) {
|
||||
$out->writeln("<error>配置文件 ".$local . " 需要更新!(缺少 `$k` 字段配置)</error>");
|
||||
$this->need_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace ZM\Command;
|
||||
use Phar;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class InitCommand extends Command
|
||||
@@ -26,6 +27,9 @@ class InitCommand extends Command
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("Initialize framework starter | 初始化框架运行的基础文件");
|
||||
$this->setDefinition([
|
||||
new InputOption("force", "F", null, "强制重制,覆盖现有文件")
|
||||
]);
|
||||
$this->setHelp("此命令将会解压以下文件到项目的根目录:\n" . implode("\n", $this->getExtractFiles()));
|
||||
// ...
|
||||
}
|
||||
@@ -34,8 +38,9 @@ class InitCommand extends Command
|
||||
if (LOAD_MODE === 1) { // 从composer依赖而来的项目模式,最基本的需要初始化的模式
|
||||
$output->writeln("<comment>Initializing files</comment>");
|
||||
$base_path = LOAD_MODE_COMPOSER_PATH;
|
||||
$args = $input->getOption("force");
|
||||
foreach ($this->extract_files as $file) {
|
||||
if (!file_exists($base_path . $file)) {
|
||||
if (!file_exists($base_path . $file) || $args) {
|
||||
$info = pathinfo($file);
|
||||
@mkdir($base_path . $info["dirname"], 0777, true);
|
||||
echo "Copying " . $file . PHP_EOL;
|
||||
@@ -67,8 +72,8 @@ class InitCommand extends Command
|
||||
}
|
||||
}
|
||||
file_put_contents($base_path . "/composer.json", json_encode($composer, 64 | 128 | 256));
|
||||
$output->writeln("<info>Executing composer update command</info>");
|
||||
exec("composer update");
|
||||
$output->writeln("<info>Executing composer command: `composer dump-autoload`</info>");
|
||||
exec("composer dump-autoload");
|
||||
echo PHP_EOL;
|
||||
} else {
|
||||
echo("Error occurred. Please check your updates.\n");
|
||||
|
||||
@@ -26,6 +26,8 @@ class RunServerCommand extends Command
|
||||
new InputOption("remote-terminal", null, null, "启用远程终端,配置使用global.php中的"),
|
||||
new InputOption("disable-coroutine", null, null, "关闭协程Hook"),
|
||||
new InputOption("daemon", null, null, "以守护进程的方式运行框架"),
|
||||
new InputOption("worker-num", null, InputOption::VALUE_REQUIRED, "启动框架时运行的 Worker 进程数量"),
|
||||
new InputOption("task-worker-num", null, InputOption::VALUE_REQUIRED, "启动框架时运行的 TaskWorker 进程数量"),
|
||||
new InputOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"),
|
||||
new InputOption("show-php-ver", null, null, "启动时显示PHP和Swoole版本"),
|
||||
new InputOption("env", null, InputOption::VALUE_REQUIRED, "设置环境类型 (production, development, staging)"),
|
||||
|
||||
@@ -13,8 +13,30 @@ class SystemdCommand extends Command
|
||||
// the name of the command (the part after "bin/console")
|
||||
protected static $defaultName = 'systemd:generate';
|
||||
|
||||
protected function configure() {
|
||||
$this->setDescription("生成框架的 systemd 配置文件");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||
//TODO: 写一个生成systemd配置的功能,给2.0
|
||||
$path = $this->generate();
|
||||
$output->writeln("<info>成功生成 systemd 文件,位置:".$path."</info>");
|
||||
$output->writeln("<info>有关如何使用 systemd 配置文件,请访问 `https://github.com/zhamao-robot/zhamao-framework/issues/36`</info>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function generate() {
|
||||
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
|
||||
$s .= "\nUser=" . exec("whoami");
|
||||
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
|
||||
$s .= "\nWorkingDirectory=" . getcwd();
|
||||
if (LOAD_MODE == 1) {
|
||||
$s .= "\nExecStart=" . getcwd() . "/vendor/bin/start server";
|
||||
} else {
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server";
|
||||
}
|
||||
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
|
||||
@mkdir(getcwd() . "/resources/");
|
||||
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
|
||||
return getcwd() . "/resources/zhamao.service";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace ZM;
|
||||
|
||||
|
||||
use Exception;
|
||||
use ZM\Command\CheckConfigCommand;
|
||||
use ZM\Command\DaemonReloadCommand;
|
||||
use ZM\Command\DaemonStatusCommand;
|
||||
use ZM\Command\DaemonStopCommand;
|
||||
@@ -14,12 +15,13 @@ use ZM\Command\RunServerCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Command\SystemdCommand;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class ConsoleApplication extends Application
|
||||
{
|
||||
const VERSION_ID = 397;
|
||||
const VERSION = "2.3.4";
|
||||
const VERSION_ID = 401;
|
||||
const VERSION = "2.4.1";
|
||||
|
||||
public function __construct(string $name = 'UNKNOWN') {
|
||||
define("ZM_VERSION_ID", self::VERSION_ID);
|
||||
@@ -73,8 +75,12 @@ class ConsoleApplication extends Application
|
||||
new DaemonStopCommand(),
|
||||
new RunServerCommand(), //运行主服务的指令控制器
|
||||
new InitCommand(), //初始化用的,用于项目初始化和phar初始化
|
||||
new PureHttpCommand() //纯HTTP服务器指令
|
||||
new PureHttpCommand(), //纯HTTP服务器指令
|
||||
new SystemdCommand()
|
||||
]);
|
||||
if (LOAD_MODE === 1) {
|
||||
$this->add(new CheckConfigCommand());
|
||||
}
|
||||
/*
|
||||
$command_register = ZMConfig::get("global", "command_register_class") ?? [];
|
||||
foreach ($command_register as $v) {
|
||||
|
||||
@@ -216,7 +216,7 @@ class Context implements ContextInterface
|
||||
* @throws WaitTimeoutException
|
||||
*/
|
||||
public function getArgs($mode, $prompt_msg) {
|
||||
$arg = ctx()->getCache("match");
|
||||
$arg = ctx()->getCache("match") ?? [];
|
||||
switch ($mode) {
|
||||
case ZM_MATCH_ALL:
|
||||
$p = $arg;
|
||||
|
||||
@@ -117,6 +117,17 @@ class EventDispatcher
|
||||
public function dispatchEvent($v, $rule_func = null, ...$params) {
|
||||
$q_c = $v->class;
|
||||
$q_f = $v->method;
|
||||
if ($q_c === "" && ($q_f instanceof \Closure)) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数的事件触发过程!");
|
||||
if ($rule_func !== null && !$rule_func($v)) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数下的 ruleFunc 判断为 false, 拒绝执行此方法。");
|
||||
$this->status = self::STATUS_RULE_FAILED;
|
||||
return false;
|
||||
}
|
||||
$this->store = $q_f(...$params);
|
||||
$this->status = self::STATUS_NORMAL;
|
||||
return true;
|
||||
}
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 ruleFunc ...");
|
||||
if ($rule_func !== null && !$rule_func($v)) {
|
||||
if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 判断为 false, 拒绝执行此方法。");
|
||||
|
||||
@@ -143,12 +143,8 @@ class ServerEventHandler
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onWorkerStart(Server $server, $worker_id) {
|
||||
zm_atomic("_#worker_".$worker_id)->set($server->worker_pid);
|
||||
//if (ZMBuf::atomic("stop_signal")->get() != 0) return;
|
||||
Process::signal(SIGUSR1, function() use ($worker_id){
|
||||
Timer::clearAll();
|
||||
ProcessManager::resumeAllWorkerCoroutines();
|
||||
});
|
||||
|
||||
Process::signal(SIGINT, function () use ($worker_id, $server) {
|
||||
Timer::clearAll();
|
||||
ProcessManager::resumeAllWorkerCoroutines();
|
||||
@@ -157,6 +153,11 @@ class ServerEventHandler
|
||||
});
|
||||
unset(Context::$context[Coroutine::getCid()]);
|
||||
if ($server->taskworker === false) {
|
||||
Process::signal(SIGUSR1, function() use ($worker_id){
|
||||
Timer::clearAll();
|
||||
ProcessManager::resumeAllWorkerCoroutines();
|
||||
});
|
||||
zm_atomic("_#worker_".$worker_id)->set($server->worker_pid);
|
||||
try {
|
||||
register_shutdown_function(function () use ($server) {
|
||||
$error = error_get_last();
|
||||
@@ -464,8 +465,12 @@ class ServerEventHandler
|
||||
}
|
||||
});
|
||||
try {
|
||||
if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) {
|
||||
if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) {
|
||||
$obb_onebot = ZMConfig::get("global", "onebot") ??
|
||||
ZMConfig::get("global", "modules")["onebot"] ??
|
||||
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
$onebot_status = $obb_onebot["status"];
|
||||
if ($conn->getName() === 'qq' && $onebot_status === true) {
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
LightCacheInside::set("connect", "conn_fd", $request->fd);
|
||||
}
|
||||
}
|
||||
@@ -510,8 +515,11 @@ class ServerEventHandler
|
||||
}
|
||||
});
|
||||
try {
|
||||
if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) {
|
||||
if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) {
|
||||
$obb_onebot = ZMConfig::get("global", "onebot") ??
|
||||
ZMConfig::get("global", "modules")["onebot"] ??
|
||||
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
if ($conn->getName() === 'qq' && $obb_onebot["status"] === true) {
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
LightCacheInside::set("connect", "conn_fd", -1);
|
||||
}
|
||||
}
|
||||
@@ -657,18 +665,19 @@ class ServerEventHandler
|
||||
}
|
||||
|
||||
//加载插件
|
||||
$plugins = ZMConfig::get("global", "modules") ?? [];
|
||||
if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
$obb_onebot = ZMConfig::get("global", "onebot") ??
|
||||
ZMConfig::get("global", "modules")["onebot"] ??
|
||||
["status" => true, "single_bot_mode" => false, "message_level" => 99999];
|
||||
|
||||
if ($plugins["onebot"]) {
|
||||
if ($obb_onebot["status"]) {
|
||||
$obj = new OnSwooleEvent();
|
||||
$obj->class = QQBot::class;
|
||||
$obj->method = 'handle';
|
||||
$obj->method = 'handleByEvent';
|
||||
$obj->type = 'message';
|
||||
$obj->level = $plugins["onebot"]["message_level"] ?? 99999;
|
||||
$obj->level = $obb_onebot["message_level"] ?? 99999;
|
||||
$obj->rule = 'connectIsQQ()';
|
||||
EventManager::addEvent(OnSwooleEvent::class, $obj);
|
||||
if ($plugins["onebot"]["single_bot_mode"]) {
|
||||
if ($obb_onebot["single_bot_mode"]) {
|
||||
LightCacheInside::set("connect", "conn_fd", -1);
|
||||
} else {
|
||||
LightCacheInside::set("connect", "conn_fd", -2);
|
||||
|
||||
@@ -89,28 +89,27 @@ class Framework
|
||||
($o = ZMConfig::get("console_color")) === false ? [] : $o
|
||||
);
|
||||
|
||||
$worker = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num();
|
||||
define("ZM_WORKER_NUM", $worker);
|
||||
ZMAtomic::init();
|
||||
$timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai";
|
||||
date_default_timezone_set($timezone);
|
||||
|
||||
$this->server_set = ZMConfig::get("global", "swoole");
|
||||
$this->server_set["log_level"] = SWOOLE_LOG_DEBUG;
|
||||
$add_port = ZMConfig::get("global", "modules")["remote_terminal"]["status"] ?? false;
|
||||
$add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false;
|
||||
|
||||
$this->parseCliArgs(self::$argv, $add_port);
|
||||
|
||||
$worker = $this->server_set["worker_num"] ?? swoole_cpu_num();
|
||||
define("ZM_WORKER_NUM", $worker);
|
||||
ZMAtomic::init();
|
||||
// 打印初始信息
|
||||
$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"];
|
||||
if (!isset($this->server_set["worker_num"])) $out["worker"] = swoole_cpu_num() . " (auto)";
|
||||
else $out["worker"] = $this->server_set["worker_num"];
|
||||
$out["environment"] = $args["env"] === null ? "default" : $args["env"];
|
||||
$out["log_level"] = Console::getLevel();
|
||||
$out["version"] = ZM_VERSION . (LOAD_MODE == 0 ? (" (build " . ZM_VERSION_ID . ")") : "");
|
||||
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"];
|
||||
if (isset($this->server_set["task_worker_num"])) {
|
||||
$out["task_worker"] = $this->server_set["task_worker_num"];
|
||||
}
|
||||
if (ZMConfig::get("global", "sql_config")["sql_host"] !== "") {
|
||||
$conf = ZMConfig::get("global", "sql_config");
|
||||
@@ -128,7 +127,7 @@ class Framework
|
||||
$out["swoole_version"] = SWOOLE_VERSION;
|
||||
}
|
||||
if ($add_port) {
|
||||
$conf = ZMConfig::get("global", "modules")["remote_terminal"];
|
||||
$conf = ZMConfig::get("global", "remote_terminal");
|
||||
$out["terminal"] = $conf["host"] . ":" . $conf["port"];
|
||||
}
|
||||
|
||||
@@ -138,30 +137,36 @@ class Framework
|
||||
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
|
||||
|
||||
if ($add_port) {
|
||||
$conf = ZMConfig::get("global", "remote_terminal") ?? [
|
||||
'status' => true,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 20002,
|
||||
'token' => ''
|
||||
];
|
||||
$welcome_msg = Console::setColor("Welcome! You can use `help` for usage.", "green");
|
||||
/** @var Port $port */
|
||||
$port = self::$server->listen("127.0.0.1", 20002, SWOOLE_SOCK_TCP);
|
||||
$port = self::$server->listen($conf["host"], $conf["port"], SWOOLE_SOCK_TCP);
|
||||
$port->set([
|
||||
'open_http_protocol' => false
|
||||
]);
|
||||
$port->on('connect', function (?\Swoole\Server $serv, $fd) use ($port, $welcome_msg) {
|
||||
$port->on('connect', function (?\Swoole\Server $serv, $fd) use ($port, $welcome_msg, $conf) {
|
||||
ManagerGM::pushConnect($fd, "terminal");
|
||||
$serv->send($fd, file_get_contents(working_dir() . "/config/motd.txt"));
|
||||
if (!empty(ZMConfig::get("global", "modules")["remote_terminal"]["token"] ?? '')) {
|
||||
if (!empty($conf["token"])) {
|
||||
$serv->send($fd, "Please input token: ");
|
||||
} else {
|
||||
$serv->send($fd, $welcome_msg . "\n>>> ");
|
||||
}
|
||||
});
|
||||
|
||||
$port->on('receive', function ($serv, $fd, $reactor_id, $data) use ($welcome_msg) {
|
||||
$port->on('receive', function ($serv, $fd, $reactor_id, $data) use ($welcome_msg, $conf) {
|
||||
ob_start();
|
||||
try {
|
||||
$arr = LightCacheInside::get("light_array", "input_token") ?? [];
|
||||
if (empty($arr[$fd] ?? '')) {
|
||||
if (ZMConfig::get("global", "modules")["remote_terminal"]["token"] != '') {
|
||||
if ($conf["token"] != '') {
|
||||
$token = trim($data);
|
||||
if ($token === ZMConfig::get("global", "modules")["remote_terminal"]["token"]) {
|
||||
if ($token === $conf["token"]) {
|
||||
SpinLock::transaction("input_token", function () use ($fd, $token) {
|
||||
$arr = LightCacheInside::get("light_array", "input_token");
|
||||
$arr[$fd] = $token;
|
||||
@@ -334,6 +339,25 @@ class Framework
|
||||
$terminal_id = uuidgen();
|
||||
foreach ($args as $x => $y) {
|
||||
switch ($x) {
|
||||
case 'worker-num':
|
||||
if ($y) {
|
||||
if (intval($y) >= 1 && intval($y) <= 1024) {
|
||||
$this->server_set["worker_num"] = intval($y);
|
||||
} else {
|
||||
Console::warning("Invalid worker num! Turn to default value (".($this->server_set["worker_num"] ?? swoole_cpu_num()).")");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'task-worker-num':
|
||||
if ($y) {
|
||||
if (intval($y) >= 1 && intval($y) <= 1024) {
|
||||
$this->server_set["task_worker_num"] = intval($y);
|
||||
$this->server_set["task_enable_coroutine"] = true;
|
||||
} else {
|
||||
Console::warning("Invalid worker num! Turn to default value (0)");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'disable-coroutine':
|
||||
if ($y) {
|
||||
$coroutine_mode = false;
|
||||
|
||||
@@ -16,6 +16,7 @@ use ZM\Exception\InterruptException;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Utils\CoMessage;
|
||||
use ZM\Utils\MessageUtil;
|
||||
use ZM\Utils\SingletonTrait;
|
||||
|
||||
/**
|
||||
* Class QQBot
|
||||
@@ -23,20 +24,28 @@ use ZM\Utils\MessageUtil;
|
||||
*/
|
||||
class QQBot
|
||||
{
|
||||
use SingletonTrait;
|
||||
|
||||
public function handleByEvent() {
|
||||
$data = json_decode(context()->getFrame()->data, true);
|
||||
$this->handle($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param int $level
|
||||
* @throws InterruptException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handle() {
|
||||
public function handle($data, $level = 0) {
|
||||
try {
|
||||
$data = json_decode(context()->getFrame()->data, true);
|
||||
if ($level > 10) return;
|
||||
set_coroutine_params(["data" => $data]);
|
||||
if (isset($data["post_type"])) {
|
||||
//echo TermColor::ITALIC.json_encode($data, 128|256).TermColor::RESET.PHP_EOL;
|
||||
ctx()->setCache("level", 0);
|
||||
ctx()->setCache("level", $level);
|
||||
//Console::debug("Calling CQ Event from fd=" . ctx()->getConnection()->getFd());
|
||||
if ($data["post_type"] != "meta_event") {
|
||||
$r = $this->dispatchBeforeEvents($data); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
$r = $this->dispatchBeforeEvents($data, "pre"); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
if ($r->store === "block") EventDispatcher::interrupt();
|
||||
}
|
||||
//Console::warning("最上数据包:".json_encode($data));
|
||||
@@ -44,6 +53,10 @@ class QQBot
|
||||
if (isset($data["echo"]) || isset($data["post_type"])) {
|
||||
if (CoMessage::resumeByWS()) EventDispatcher::interrupt();
|
||||
}
|
||||
if (($data["post_type"] ?? "meta_event") != "meta_event") {
|
||||
$r = $this->dispatchBeforeEvents($data, "post"); // before在这里执行,元事件不执行before为减少不必要的调试日志
|
||||
if ($r->store === "block") EventDispatcher::interrupt();
|
||||
}
|
||||
if (isset($data["post_type"])) $this->dispatchEvents($data);
|
||||
else $this->dispatchAPIResponse($data);
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
|
||||
@@ -53,14 +66,21 @@ class QQBot
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $time
|
||||
* @return EventDispatcher
|
||||
* @throws Exception
|
||||
*/
|
||||
public function dispatchBeforeEvents($data): EventDispatcher {
|
||||
public function dispatchBeforeEvents($data, $time): EventDispatcher {
|
||||
$before = new EventDispatcher(CQBefore::class);
|
||||
$before->setRuleFunction(function ($v) use ($data) {
|
||||
return $v->cq_event == $data["post_type"];
|
||||
});
|
||||
if ($time === "pre") {
|
||||
$before->setRuleFunction(function($v) use ($data){
|
||||
return $v->level >= 200 && $v->cq_event == $data["post_type"];
|
||||
});
|
||||
} else {
|
||||
$before->setRuleFunction(function($v) use ($data){
|
||||
return $v->level < 200 && $v->cq_event == $data["post_type"];
|
||||
});
|
||||
}
|
||||
$before->setReturnFunction(function ($result) {
|
||||
if (!$result) EventDispatcher::interrupt("block");
|
||||
});
|
||||
|
||||
@@ -9,7 +9,6 @@ use ZM\API\CQ;
|
||||
use ZM\Config\ZMConfig;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Entity\MatchResult;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Requests\ZMRequest;
|
||||
|
||||
@@ -110,7 +109,7 @@ class MessageUtil
|
||||
* @return MatchResult
|
||||
*/
|
||||
public static function matchCommand($msg, $obj) {
|
||||
$ls = EventManager::$events[CQCommand::class];
|
||||
$ls = EventManager::$events[CQCommand::class] ?? [];
|
||||
$word = self::splitCommand($msg);
|
||||
$matched = new MatchResult();
|
||||
foreach ($ls as $k => $v) {
|
||||
@@ -125,12 +124,12 @@ class MessageUtil
|
||||
$matched->object = $v;
|
||||
$matched->status = true;
|
||||
break;
|
||||
} elseif ($v->start_with != "" && mb_strpos($msg, $v->start_with) === 0) {
|
||||
} elseif ($v->start_with != "" && mb_substr($msg, 0, mb_strlen($v->start_with)) === $v->start_with) {
|
||||
$matched->match = [mb_substr($msg, mb_strlen($v->start_with))];
|
||||
$matched->object = $v;
|
||||
$matched->status = true;
|
||||
break;
|
||||
} elseif ($v->end_with != "" && strlen($msg) == (strripos($msg, $v->end_with) + strlen($v->end_with))) {
|
||||
} elseif ($v->end_with != "" && mb_substr($msg, 0 - mb_strlen($v->end_with)) === $v->end_with) {
|
||||
$matched->match = [substr($msg, 0, strripos($msg, $v->end_with))];
|
||||
$matched->object = $v;
|
||||
$matched->status = true;
|
||||
@@ -160,4 +159,10 @@ class MessageUtil
|
||||
}
|
||||
return $matched;
|
||||
}
|
||||
|
||||
public static function addShortCommand($command, string $reply) {
|
||||
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
ProcessManager::sendActionToWorker($i, "add_short_command", [$command, $reply]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Swoole\OnPipeMessageEvent;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Event\EventDispatcher;
|
||||
use ZM\Event\EventManager;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\LightCacheInside;
|
||||
use ZM\Store\WorkerCache;
|
||||
@@ -17,6 +19,14 @@ class ProcessManager
|
||||
public static function workerAction($src_worker_id, $data) {
|
||||
$server = server();
|
||||
switch ($data["action"] ?? '') {
|
||||
case 'add_short_command':
|
||||
Console::verbose("Adding short command ".$data["data"][0]);
|
||||
$obj = new CQCommand();
|
||||
$obj->method = quick_reply_closure($data["data"][1]);
|
||||
$obj->match = $data["data"][0];
|
||||
$obj->class = "";
|
||||
EventManager::addEvent(CQCommand::class, $obj);
|
||||
break;
|
||||
case "eval":
|
||||
eval($data["data"]);
|
||||
break;
|
||||
|
||||
@@ -33,7 +33,7 @@ class Terminal
|
||||
$help[] = "bc:\t\teval执行代码,但输入必须是将代码base64之后的,如 `bc em1faW5mbygn5L2g5aW9Jyk7`";
|
||||
$help[] = "stop:\t\t停止服务器";
|
||||
$help[] = "reload | r:\t热重启用户编写的模块代码";
|
||||
foreach(EventManager::$events[TerminalCommand::class] as $v) {
|
||||
foreach((EventManager::$events[TerminalCommand::class] ?? []) as $v) {
|
||||
$help[]=$v->command.":\t\t".(empty($v->description) ? "<用户自定义指令>" : $v->description);
|
||||
}
|
||||
echo implode("\n", $help) . PHP_EOL;
|
||||
|
||||
@@ -22,8 +22,9 @@ class ZMUtil
|
||||
Console::warning(Console::setColor("Stopping server...", "red"));
|
||||
if (Console::getLevel() >= 4) Console::trace();
|
||||
ZMAtomic::get("stop_signal")->set(1);
|
||||
for($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
Process::kill(zm_atomic("_#worker_".$i)->get(), SIGUSR1);
|
||||
for ($i = 0; $i < ZM_WORKER_NUM; ++$i) {
|
||||
if (Process::kill(zm_atomic("_#worker_" . $i)->get(), 0))
|
||||
Process::kill(zm_atomic("_#worker_" . $i)->get(), SIGUSR1);
|
||||
}
|
||||
server()->shutdown();
|
||||
server()->stop();
|
||||
|
||||
@@ -394,3 +394,9 @@ function zm_verbose($obj) { Console::verbose($obj); }
|
||||
function zm_error($obj) { Console::error($obj); }
|
||||
|
||||
function zm_config($name, $key = null) { return ZMConfig::get($name, $key); }
|
||||
|
||||
function quick_reply_closure($reply) {
|
||||
return function() use ($reply){
|
||||
return $reply;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user