mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-02 22:35:38 +08:00
Compare commits
237 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5a6f1fea4 | ||
|
|
ba2777137b | ||
|
|
6d90be164a | ||
|
|
4da6f5859a | ||
|
|
15d4ea710a | ||
|
|
f0541c1f32 | ||
|
|
2b8cab1824 | ||
|
|
61c7972915 | ||
|
|
44a0eec74c | ||
|
|
e57753e44b | ||
|
|
74e91a2950 | ||
|
|
7ce3ef41df | ||
|
|
444a77933a | ||
|
|
78f78c607d | ||
|
|
69155002dc | ||
|
|
475d14fab7 | ||
|
|
f4d7e63358 | ||
|
|
f222d2b45b | ||
|
|
d0155fe1da | ||
|
|
4737d0b507 | ||
|
|
09bd0197bb | ||
|
|
f0f120bd32 | ||
|
|
c897da29c6 | ||
|
|
e347e254e8 | ||
|
|
12363aebf0 | ||
|
|
ff0b925313 | ||
|
|
a6b4bd9b80 | ||
|
|
485fa5476c | ||
|
|
689076d97c | ||
|
|
cca6102e91 | ||
|
|
095855162b | ||
|
|
326f934013 | ||
|
|
35b0c258fe | ||
|
|
6650846b15 | ||
|
|
a33d320f4c | ||
|
|
0bcfea6aa4 | ||
|
|
db6e63e91c | ||
|
|
a7f84fb53a | ||
|
|
ce7f2b1765 | ||
|
|
abbfb59eff | ||
|
|
b57fef16f9 | ||
|
|
1706afbcd0 | ||
|
|
d01bd69aa5 | ||
|
|
e6b9ae3ee1 | ||
|
|
73b6b8045d | ||
|
|
3c87abc6e8 | ||
|
|
e95925c129 | ||
|
|
82c44d6c40 | ||
|
|
e0a268e05e | ||
|
|
487892e1d9 | ||
|
|
7ab4e88359 | ||
|
|
4702b6ee75 | ||
|
|
20cd3aa66d | ||
|
|
391114bdef | ||
|
|
ffe1052ecc | ||
|
|
b4159152a7 | ||
|
|
8fc6e4b0f7 | ||
|
|
c8938b7a4b | ||
|
|
34db1626a5 | ||
|
|
7f0c97c5b9 | ||
|
|
74050c46e7 | ||
| 3ed1cb665a | |||
|
|
a6f33ba69d | ||
|
|
176b690417 | ||
|
|
e22b1b90ec | ||
|
|
a3c560790c | ||
| 570e2108dd | |||
|
|
59c0d95e5d | ||
|
|
9ceaecdc02 | ||
|
|
19d50898ef | ||
|
|
5b62ca62ae | ||
| fb528d30ce | |||
| 5f2d5ed334 | |||
| c20e459900 | |||
| 09220825cf | |||
|
|
3d4db23d27 | ||
|
|
4496b67dcc | ||
|
|
2a13298384 | ||
|
|
d6ec404d76 | ||
|
|
3235fd4dc1 | ||
|
|
293740fee2 | ||
|
|
d3c420ec84 | ||
|
|
85ef09d43c | ||
|
|
e4561d69c4 | ||
|
|
6b4d206099 | ||
|
|
50843edf6a | ||
|
|
3a1686f8da | ||
|
|
66dd91bb97 | ||
|
|
e020e5d593 | ||
|
|
3d62663281 | ||
|
|
8d9485c02e | ||
|
|
9fb45dd683 | ||
|
|
8a4924dba9 | ||
|
|
beaf7be606 | ||
|
|
7dbd21bdf4 | ||
|
|
9ce3056203 | ||
|
|
880b4e847c | ||
|
|
71e83d5bc8 | ||
|
|
432fd92cca | ||
|
|
907a9cea25 | ||
| 07391810ff | |||
| 5063421364 | |||
| 2b4d308783 | |||
| e2f49968b3 | |||
| 229778ebf9 | |||
|
|
d2c0972c93 | ||
|
|
d300b6e518 | ||
|
|
56cb7b2223 | ||
|
|
2fc42d5d60 | ||
|
|
999e90f709 | ||
|
|
45c6cd7d2a | ||
|
|
5aa0858021 | ||
|
|
e981da3932 | ||
|
|
2685be7306 | ||
|
|
a13c4628f5 | ||
|
|
b36417454c | ||
|
|
a6f5952dee | ||
|
|
d67dfe46f6 | ||
|
|
e57cc43500 | ||
|
|
481063285b | ||
|
|
d805523dbd | ||
|
|
58267f66fc | ||
|
|
48215f2e5e | ||
|
|
7e0fc1528a | ||
|
|
c185d20a93 | ||
|
|
7ec847e576 | ||
|
|
4ee16d4fc6 | ||
|
|
59d614a24e | ||
|
|
40037b3f4b | ||
|
|
f91c8b6205 | ||
|
|
8f73a99ff7 | ||
|
|
e0f07cb396 | ||
|
|
2950ab7472 | ||
|
|
0ab4053dfb | ||
|
|
745aa0f268 | ||
|
|
12bb93c2f0 | ||
| 95ca175901 | |||
| 71585ed29d | |||
|
|
231a377718 | ||
|
|
a80ee902a9 | ||
|
|
c2d3b5f92a | ||
|
|
64365af124 | ||
|
|
60619dbffc | ||
|
|
77e77e9cc3 | ||
|
|
d72b41a902 | ||
|
|
dfddaaea94 | ||
|
|
6b872c6f74 | ||
|
|
202c8aee77 | ||
|
|
ba397a1744 | ||
|
|
d699a152d5 | ||
|
|
beef44ea50 | ||
|
|
b991a2da7b | ||
|
|
bbe4addd83 | ||
|
|
600829645d | ||
|
|
68280cfe7e | ||
|
|
c5523aa95d | ||
|
|
93a68a5582 | ||
|
|
6155236d3c | ||
| 28f7f20728 | |||
| 235256d679 | |||
| 626d569858 | |||
| 0492179bdd | |||
| 1dfd1de5c1 | |||
| d15d01c32b | |||
|
|
c9f4278d9b | ||
|
|
6aa0540c9e | ||
|
|
9689dc9db1 | ||
|
|
c20e3324d4 | ||
| 303f44cd2b | |||
| 66b73973b4 | |||
|
|
0ff4e52ed3 | ||
| b6d1f724e9 | |||
| e77b9d4970 | |||
|
|
456b102c15 | ||
|
|
cc57997abc | ||
|
|
19e61c7cc3 | ||
|
|
f908513dca | ||
|
|
7dc39e6ada | ||
|
|
b0be53554d | ||
|
|
b98048bd39 | ||
|
|
fffd3fdc95 | ||
|
|
dea9ed2ccd | ||
|
|
de5744c9e4 | ||
|
|
a23f3d8f16 | ||
|
|
0c24bfdedd | ||
|
|
c0b95c6840 | ||
|
|
e977b09e20 | ||
|
|
4ff75cf199 | ||
|
|
24e70c70ce | ||
|
|
275a7bf00b | ||
|
|
455fc79818 | ||
|
|
8740c3c255 | ||
|
|
98bfca5bb9 | ||
|
|
fc8d01ad5f | ||
|
|
d9b8df1725 | ||
|
|
9b7802ac04 | ||
|
|
6e1f3e0406 | ||
|
|
a2d4bab062 | ||
|
|
f1cefad910 | ||
|
|
957c69bd1e | ||
|
|
2902c5e805 | ||
|
|
faf9f5d988 | ||
|
|
874f061468 | ||
|
|
69521a1f1f | ||
|
|
fb9dbed306 | ||
|
|
d42158ac90 | ||
|
|
ff3ebec562 | ||
|
|
ea79de617e | ||
|
|
9e1ad6a983 | ||
|
|
c17248df31 | ||
|
|
4c116ebd86 | ||
|
|
c490fe0c1c | ||
|
|
cefdf23799 | ||
|
|
7f70642606 | ||
|
|
1d5b2609f9 | ||
|
|
a206fe8b87 | ||
|
|
fb4f6c45ce | ||
|
|
c50ae245bd | ||
|
|
f6c2131ebf | ||
|
|
543d1d2922 | ||
|
|
bb61e6f6a2 | ||
|
|
2d1bbf6b48 | ||
|
|
67e42cfe3e | ||
|
|
429a2cf230 | ||
|
|
9ace85e604 | ||
|
|
f677b0e132 | ||
|
|
f137f044d0 | ||
|
|
77c12db31a | ||
|
|
b670cb29fe | ||
|
|
95d7bb071d | ||
|
|
eadb4c1dee | ||
|
|
6672a6c852 | ||
|
|
094feddda4 | ||
|
|
f86eddb298 | ||
|
|
a93b4917cd | ||
|
|
0f9767aa16 | ||
|
|
0c9f246690 |
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
if [ ! -d "/app/zhamao-framework/bin" ]; then
|
||||
cp -r /app/zhamao-framework-bak/* /app/zhamao-framework/
|
||||
fi
|
||||
php /app/zhamao-framework/bin/start
|
||||
38
.github/ISSUE_TEMPLATE/1_Bug_report.yaml
vendored
Normal file
38
.github/ISSUE_TEMPLATE/1_Bug_report.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: 🐛 漏洞(BUG)报告
|
||||
description: ⚠️ 请不要直接在此提交安全漏洞
|
||||
labels: bug
|
||||
|
||||
body:
|
||||
- type: input
|
||||
id: affected-versions
|
||||
attributes:
|
||||
label: 受影响版本
|
||||
placeholder: x.y.z
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请详细地描述您的问题
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce-steps
|
||||
attributes:
|
||||
label: 复现步骤
|
||||
description: |
|
||||
请尽可能地提供可以复现此步骤的漏洞。
|
||||
如果步骤过长或难以描述,您可以自行建立一个用于复现漏洞的仓库。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: possible-solution
|
||||
attributes:
|
||||
label: 解决方案
|
||||
description: 如果您对这个漏洞的成因或修复有任何意见的话,请在此提出
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: 附加信息
|
||||
description: 其他可能有帮助的信息,如日志、截图等
|
||||
19
.github/ISSUE_TEMPLATE/2_Feature_request.yaml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/2_Feature_request.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: 🚀 功能建议
|
||||
description: 新功能、改进的意见、草案
|
||||
labels: enhancement
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 描述
|
||||
description: 请提供简洁清楚的描述
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: example
|
||||
attributes:
|
||||
label: 例子
|
||||
description: |
|
||||
一个简单的例子,展示该功能将如何被使用(包括代码、配置文件等)
|
||||
如果这是针对已有功能的改进,请展示改进前后使用方式(或效能)的对比
|
||||
59
.github/workflows/integration-test.yml
vendored
Normal file
59
.github/workflows/integration-test.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Integration Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
- review_requested
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
name: Integration Test (PHP ${{ matrix.php-versions }}) (OS ${{ matrix.operating-system }})
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: [ "ubuntu-latest", "macos-latest" ]
|
||||
php-versions: [ "7.2", "7.3", "7.4", "8.0", "8.1" ]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: swoole, posix, json
|
||||
|
||||
- name: Setup problem matchers for PHP
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
|
||||
- name: Validate composer.json
|
||||
run: "composer validate --strict"
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install Composer Dependencies
|
||||
run: "composer install --prefer-dist --no-progress --optimize-autoloader"
|
||||
|
||||
- name: Run Static Analysis
|
||||
run: "composer analyse"
|
||||
|
||||
- name: Run PHP CS Fixer Check
|
||||
run: "./vendor/bin/php-cs-fixer fix --dry-run --diff"
|
||||
33
.github/workflows/mkdocs-deploy.yml
vendored
Normal file
33
.github/workflows/mkdocs-deploy.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: MkDocs Auto Deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Deploy docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout master
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Deploy docs to GitHub Pages
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CUSTOM_DOMAIN: framework.zhamao.me
|
||||
CONFIG_FILE: mkdocs.yml
|
||||
EXTRA_PACKAGES: build-base
|
||||
- name: Copy deployment to current folder
|
||||
run: |
|
||||
cp -r "${GITHUB_WORKSPACE}/site" "./deploy"
|
||||
- name: Deploy to Zhamao Server
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.ZHAMAO_XIN_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "deploy/"
|
||||
REMOTE_HOST: ${{ secrets.ZHAMAO_XIN_HOST }}
|
||||
REMOTE_USER: ${{ secrets.ZHAMAO_XIN_USER }}
|
||||
TARGET: ${{ secrets.ZHAMAO_XIN_TARGET }}
|
||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -2,7 +2,6 @@
|
||||
/src/test/
|
||||
/src/webconsole/config/
|
||||
/vendor/
|
||||
zm.json
|
||||
/zm_data/
|
||||
composer.lock
|
||||
/resources/server.phar
|
||||
@@ -10,4 +9,21 @@ composer.lock
|
||||
/bin/.phpunit.result.cache
|
||||
/resources/zhamao.service
|
||||
.phpunit.result.cache
|
||||
.daemon_pid
|
||||
.daemon_pid
|
||||
/runtime/
|
||||
/tmp/
|
||||
/temp/
|
||||
/site/
|
||||
|
||||
# go-cqhttp快速安装启动相关(可能被废弃)
|
||||
/ext/go-cqhttp/data/
|
||||
/ext/go-cqhttp/logs/
|
||||
/ext/go-cqhttp/config.hjson
|
||||
/ext/go-cqhttp/device.json
|
||||
/ext/go-cqhttp/go-cqhttp
|
||||
/ext/go-cqhttp/session.token
|
||||
|
||||
.zm_worker_*.pid
|
||||
|
||||
# Git Hook 的相关锁文件
|
||||
cghooks.lock
|
||||
75
.php-cs-fixer.php
Normal file
75
.php-cs-fixer.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @since 2.7.0
|
||||
*/
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'@Symfony' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'list_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one',
|
||||
],
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare',
|
||||
],
|
||||
],
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'single_line_comment_style' => [
|
||||
'comment_types' => [
|
||||
],
|
||||
],
|
||||
'yoda_style' => [
|
||||
'always_move_variable' => false,
|
||||
'equal' => false,
|
||||
'identical' => false,
|
||||
],
|
||||
'multiline_whitespace_before_semicolons' => [
|
||||
'strategy' => 'no_multi_line',
|
||||
],
|
||||
'constant_case' => [
|
||||
'case' => 'lower',
|
||||
],
|
||||
'class_attributes_separation' => true,
|
||||
'combine_consecutive_unsets' => true,
|
||||
'declare_strict_types' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_unused_imports' => true,
|
||||
'not_operator_with_successor_space' => false,
|
||||
'not_operator_with_space' => false,
|
||||
'ordered_class_elements' => true,
|
||||
'php_unit_strict' => false,
|
||||
'phpdoc_separation' => false,
|
||||
'single_quote' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'multiline_comment_opening_closing' => true,
|
||||
'phpdoc_summary' => false,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->exclude('vendor')
|
||||
->exclude('docs')
|
||||
->in(__DIR__ . '/src')
|
||||
)
|
||||
->setUsingCache(false);
|
||||
17
.run/Reload Zhamao Server.run.xml
Normal file
17
.run/Reload Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Reload Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server:reload && exit" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Run Zhamao Server.run.xml
Normal file
17
.run/Run Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="./zhamao" />
|
||||
<option name="SCRIPT_OPTIONS" value="server" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Run watcher.run.xml
Normal file
17
.run/Run watcher.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run watcher" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="bin/watcher" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
17
.run/Stop Zhamao Server.run.xml
Normal file
17
.run/Stop Zhamao Server.run.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Stop Zhamao Server" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="./zhamao server:stop && exit" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM zmbot/swoole:latest
|
||||
|
||||
# TODO: auto-setup entrypoint
|
||||
79
README.md
79
README.md
@@ -1,26 +1,24 @@
|
||||
<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>
|
||||
炸毛框架 (zhamao-framework) 是一个协程高性能的聊天机器人 + Web 服务器开发框架<br><br>
|
||||
|
||||
[]()
|
||||
[](http://wpa.qq.com/msgrd?v=3&uin=627577391&site=qq&menu=yes)
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
|
||||
[](https://packagist.org/packages/zhamao/framework)
|
||||
[]()
|
||||
[](https://github.com/howmanybots/onebot)
|
||||

|
||||
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=AnnotationBase)
|
||||
[](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
|
||||
|
||||
</div>
|
||||
|
||||
## 开发者注意
|
||||
**开发者 QQ 群:670821194**
|
||||
开发者 QQ 群:**670821194** [点击加入群聊](https://jq.qq.com/?_wv=1027&k=YkNI3AIr)
|
||||
|
||||
**当前 v2 版本已正式发布,此 master 分支为 2.0 版本,如需查看 v1 版本,请移步 `v1-legacy` 分支!**
|
||||
**如果有愿意一起开发框架本身的开发者,请提出 PR 或 Issue 参与开发!如果对框架本身的核心设计有更好的想法,可与作者成立开发组(目前仅作者 1 人),参与 OneBot V12 生态和框架本身的开发。**
|
||||
|
||||
**2.0 版本如果有问题请第一时间加群反馈!**
|
||||
|
||||
有关 3.0 版本的最新情况,请看这里:[Issue #22](https://github.com/zhamao-robot/zhamao-framework/issues/22)
|
||||
**相关正在进行的版本任务见 Projects 一栏!**
|
||||
|
||||
## 简介
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(OneBot 兼容的 QQ 机器人对接),包含 Websocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
@@ -43,50 +41,59 @@ public function index() {
|
||||
```
|
||||
|
||||
## 开始
|
||||
框架首先需要部署环境,可以参考下方文档中部署环境和框架的方法进行。
|
||||
如果你是初学者,可以直接使用以下脚本部署 PHP 环境和安装框架的脚手架:
|
||||
```bash
|
||||
# 新建一个自己喜欢名字的文件夹,运行一键安装脚本 (仅限 x86_64 和 aarch64 平台)
|
||||
mkdir zhamao-app/
|
||||
cd zhamao-app/
|
||||
bash -c "$(curl -fsSL https://api.zhamao.xin/go.sh)"
|
||||
|
||||
# 启动
|
||||
vendor/bin/start server
|
||||
```
|
||||
|
||||
## 文档(v2 版本)
|
||||
查看文档:[https://docs-v2.zhamao.xin/](https://docs-v2.zhamao.xin/)
|
||||
查看文档(国内自建):<https://framework.zhamao.xin/>
|
||||
|
||||
备用链接:[https://docs-v2.zhamao.me/](https://docs-v2.zhamao.me/)
|
||||
备用链接(国外托管):<https://framework.zhamao.me/>
|
||||
|
||||
自行构建文档:`mkdocs build -d distribute`
|
||||
|
||||
## 特点
|
||||
- 支持多账号
|
||||
- 原生为多账号设计,支持多个机器人负载均衡
|
||||
- 使用 Swoole 多工作进程机制和协程加持,尽可能简单的情况下提升了性能
|
||||
- 灵活的注解事件绑定机制
|
||||
- 支持下断点调试(Psysh)
|
||||
- 灵活的注解事件绑定机制,可兼容使用 PHP8 的 Attribute(>=2.7 可用)
|
||||
- 易用的上下文,模块内随处可用
|
||||
- 采用模块化编写,可单独拆装功能
|
||||
- 常驻内存,全局缓存变量随处使用
|
||||
- 采用模块化编写,可自由搭配其他 composer 组件,也可单文件面向过程编写
|
||||
- 常驻内存,全局缓存变量随处使用,提供多种缓存方案
|
||||
- 自带 MySQL、Redis 等数据库连接池等数据库连接方案
|
||||
- 自带 HTTP 服务器、WebSocket 服务器可复用,可以构建属于自己的 HTTP API 接口
|
||||
- 静态文件服务器
|
||||
- 本身为 HTTP 服务器、WebSocket 服务器,可以构建属于自己的 HTTP API 接口
|
||||
- 静态文件服务器,可将前端合并到一起
|
||||
- 自带 PHP + Swoole 环境,无需手动编译安装,by [crazywhalecc/static-php-cli](https://github.com/crazywhalecc/static-php-cli)
|
||||
|
||||
## 从 v1 升级
|
||||
炸毛框架 v2 相对 v1 版本改动了不少内容,其中包括框架底层机制、注解事件分发、调试、命名空间等变化,详情可查看上方文档。
|
||||
## 下载源码
|
||||
框架源码可直接克隆本仓库进行编辑,如果你在国内,访问 GitHub 和 clone 仓库比较慢,可以将 `github.com` 替换为 `fgit.zhamao.me` 进行加速。
|
||||
|
||||
如果旧版框架使用过程中无问题且对新功能暂无需求,可以继续使用 v1 版本,后续也将维护安全类更新和修复致命 bug。
|
||||
例如:`git clone https://fgit.zhamao.me/zhamao-robot/zhamao-framework.git --depth 1`。
|
||||
|
||||
## 贡献和捐赠
|
||||
如果你在使用过程中发现任何问题,可以提交 Issue 或自行 Fork 后修改并提交 Pull Request。目前项目仅一人维护,耗费精力较大,所以非常欢迎对框架的贡献。
|
||||
如果你在使用过程中发现任何问题,可以提交 Issue 或自行 Fork 后修改并提交 Pull Request。
|
||||
|
||||
目前项目仅一人维护,耗费精力较大,所以非常欢迎对框架的贡献。
|
||||
|
||||
本项目为作者闲暇时间开发,如果觉得好用,不妨进行捐助~你的捐助会让我更加有动力完善插件,感谢你的支持!
|
||||
|
||||
我们会将捐赠的资金用于本项目驱动的炸毛机器人和框架文档的服务器开销上。
|
||||
我们会将捐赠的资金用于本项目驱动的炸毛机器人和框架文档的服务器开销上。[捐赠列表](https://github.com/zhamao-robot/thanks)
|
||||
|
||||
### 支付宝
|
||||

|
||||
|
||||
如果你对我们的周边感兴趣,我们还有炸毛机器人定制 logo 的雨伞,详情咨询作者 QQ,我们会作为您捐助了本项目!
|
||||

|
||||
|
||||
## 关于
|
||||
框架和 SDK 是 炸毛机器人 项目的核心框架开源部分。炸毛机器人是作者写的一个高性能机器人,曾获全国计算机设计大赛一等奖。
|
||||
|
||||
作者的炸毛机器人已从2018年初起稳定运行了**三年**,并且持续迭代。
|
||||
作者的炸毛机器人已从2018年初起稳定运行了**四年半**,并且持续迭代。
|
||||
|
||||
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ(627577391)或提交 Issue 进行疑难解答。
|
||||
欢迎随时在 HTTP-API 插件群里提问,当然更好的话可以加作者 QQ([627577391](http://wpa.qq.com/msgrd?v=3&uin=627577391&site=qq&menu=yes))或提交 Issue 进行疑难解答。
|
||||
|
||||
本项目在更新内容时,请及时关注 GitHub 动态,更新前请将自己的模块代码做好备份。
|
||||
|
||||
@@ -94,4 +101,12 @@ public function index() {
|
||||
|
||||
**注意**:在你使用 mirai 等 `AGPL-3.0` 协议的机器人软件与框架连接时,使用本框架需要将你编写或修改的部分使用 `AGPL-3.0` 协议重新分发。
|
||||
|
||||

|
||||
在贡献代码时,请保管好自己的全局配置文件中的敏感信息,请勿将带有个人信息的配置文件上传 GitHub 等网站。
|
||||
|
||||
感谢 JetBrains 为此开源项目提供 PhpStorm 开发工具支持:
|
||||
|
||||
<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/PhpStorm.svg" width="300">
|
||||
|
||||
感谢 [php-libonebot](https://github.com/botuniverse/php-libonebot) 开发者 @sunxyw 中为项目开发规范化提出的一些建议。
|
||||
|
||||
<!--  -->
|
||||
|
||||
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.
|
||||
20
bin/phpunit-swoole
Executable file → Normal file
20
bin/phpunit-swoole
Executable file → Normal file
@@ -6,13 +6,15 @@
|
||||
* Date: 2018/4/14 下午10:58
|
||||
*/
|
||||
|
||||
Co::set([
|
||||
'log_level' => SWOOLE_LOG_INFO,
|
||||
use Swoole\Coroutine;
|
||||
|
||||
Coroutine::set([
|
||||
'log_level' => SWOOLE_LOG_INFO,
|
||||
'trace_flags' => 0
|
||||
]);
|
||||
|
||||
if (!ini_get('date.timezone')) {
|
||||
ini_set('date.timezone', 'UTC');
|
||||
ini_set('date.timezone', 'Asia/Shanghai');
|
||||
}
|
||||
|
||||
foreach ([
|
||||
@@ -52,14 +54,16 @@ if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require PHPUNIT_COMPOSER_INSTALL;
|
||||
$starttime = microtime(true);
|
||||
go(function (){
|
||||
try{
|
||||
go(function () {
|
||||
try {
|
||||
require_once __DIR__.'/../test/bootstrap.php';
|
||||
PHPUnit\TextUI\Command::main(false);
|
||||
} catch(Exception $e) {
|
||||
echo $e->getMessage().PHP_EOL;
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
});
|
||||
Swoole\Event::wait();
|
||||
echo "Took ".round(microtime(true) - $starttime, 4). "s\n";
|
||||
echo "Took " . round(microtime(true) - $starttime, 4) . "s\n";
|
||||
|
||||
44
bin/start
44
bin/start
@@ -1,14 +1,34 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
#!/bin/sh
|
||||
|
||||
if (!is_dir(__DIR__ . '/../vendor')) {
|
||||
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";
|
||||
}
|
||||
# shellcheck disable=SC2068
|
||||
# shellcheck disable=SC2181
|
||||
# author: crazywhalecc
|
||||
# since: 2.5.0
|
||||
|
||||
(new ZM\ConsoleApplication("zhamao-framework"))->initEnv()->run();
|
||||
if [ -f "$(pwd)/runtime/php" ]; then
|
||||
executable="$(pwd)/runtime/php"
|
||||
echo "* Framework started with built-in php."
|
||||
else
|
||||
which php >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
executable=$(which php)
|
||||
else
|
||||
echo '[ErrCode:E00014] Cannot find any PHP runtime, please use command "./install-runtime.sh" or install PHP manually!'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
result=$(echo "$1" | grep -E "module|build")
|
||||
|
||||
if [ "$result" != "" ]; then
|
||||
executable="$executable -d phar.readonly=off"
|
||||
fi
|
||||
|
||||
if [ -f "$(pwd)/src/entry.php" ]; then
|
||||
$executable "$(pwd)/src/entry.php" $@
|
||||
elif [ -f "$(pwd)/vendor/zhamao/framework/src/entry.php" ]; then
|
||||
$executable "$(pwd)/vendor/zhamao/framework/src/entry.php" $@
|
||||
else
|
||||
echo "[ErrCode:E00015] Cannot find zhamao-framework entry file!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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 --disable-console-input";
|
||||
else
|
||||
$s .= "\nExecStart=" . getcwd() . "/bin/start server --disable-console-input";
|
||||
$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";
|
||||
}
|
||||
118
bin/watcher
Executable file
118
bin/watcher
Executable file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
_red="\e[33m"
|
||||
_green="\e[32m"
|
||||
_reset="\e[0m"
|
||||
|
||||
unix_s=$(uname -s)
|
||||
|
||||
unix_release=$(
|
||||
marked_release=""
|
||||
if [ "$unix_s" = "Linux" ]; then
|
||||
echo $HOME | grep com.termux >/dev/null
|
||||
if [ $? == 0 ]; then
|
||||
marked_release="termux"
|
||||
elif [ -f "/etc/redhat-release" ]; then
|
||||
if [ "$(cat /etc/redhat-release | awk '{print $1}' | grep -v '^$')" = "CentOS" ]; then
|
||||
marked_release="CentOS"
|
||||
else
|
||||
marked_release="unknown"
|
||||
fi
|
||||
elif [ -f "/etc/os-release" ]; then
|
||||
cat /etc/os-release | grep Alpine > /dev/null
|
||||
if [ $? == 0 ]; then
|
||||
marked_release="Alpine"
|
||||
fi
|
||||
fi
|
||||
if [ "$marked_release" = "" ]; then
|
||||
if [ -f "/etc/issue" ]; then
|
||||
marked_release=$(cat /etc/issue | grep -v '^$' | awk '{print $1}')
|
||||
else
|
||||
marked_release="unknown"
|
||||
fi
|
||||
fi
|
||||
elif [ "$unix_s" = "Darwin" ]; then
|
||||
marked_release=$(sw_vers | grep ProductName | awk '{print $2" "$3" "$4}')
|
||||
fi
|
||||
echo $marked_release
|
||||
)
|
||||
unix_release=$(echo $unix_release | xargs)
|
||||
|
||||
function echo_error() {
|
||||
echo -e "${_red}$1${_reset}"
|
||||
}
|
||||
|
||||
function echo_info() {
|
||||
echo -e "${_green}$1${_reset}"
|
||||
}
|
||||
|
||||
function install_test() {
|
||||
which fswatch >/dev/null
|
||||
if [ $? != 0 ]; then
|
||||
operate_confirm "fswatch还没有安装,是否确认安装?" && install_fswatch
|
||||
fi
|
||||
}
|
||||
function install_fswatch() {
|
||||
if [ "$unix_s" = "Linux" ]; then
|
||||
case $unix_release in
|
||||
"Kali" | "Ubuntu" | "Debian" | "Raspbian" | 'Pop!_OS')
|
||||
sudo apt-get install fswatch -y ;;
|
||||
#"termux") pkg install $1 -y ;;
|
||||
"CentOS")
|
||||
curl -o fswatch.tgz https://download.fastgit.org/emcrisostomo/fswatch/releases/download/1.16.0/fswatch-1.16.0.tar.gz && \
|
||||
tar -xzf fswatch.tgz && \
|
||||
cd fswatch-1.16.0 && \
|
||||
./configure && \
|
||||
make && \
|
||||
sudo make install && \
|
||||
cd .. && \
|
||||
rm -rf fswatch.tgz fswatch-1.16.0
|
||||
;;
|
||||
#"Alpine") apk add $1 ;;
|
||||
*)
|
||||
echo_error "不支持的 Linux 发行版:$unix_release"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
elif [ "$unix_s" = "Darwin" ]; then
|
||||
brew install fswatch
|
||||
else
|
||||
echo_error "不支持的操作系统:$unix_s"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
function operate_confirm() {
|
||||
echo -n $(echo_info "$1 [Y/n] ")
|
||||
read operate
|
||||
operate=$(echo $operate | tr A-Z a-z)
|
||||
if [[ "$operate" = "y" || "$operate" = "" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo_info "当前系统:$unix_release"
|
||||
install_test
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
else
|
||||
echo_info "程序路径:$(which fswatch)"
|
||||
fi
|
||||
watch_dir="./src"
|
||||
if [ ! -d "$watch_dir" ]; then
|
||||
echo_error "src目录不存在!"
|
||||
exit 1
|
||||
else
|
||||
echo_info "监听目录:$watch_dir"
|
||||
fi
|
||||
_pid=$(cat .daemon_pid | awk -F"\"pid\": " '{print $2}' | grep -v ^$ | sed 's/,//g')
|
||||
if [ "$_pid" = "" ]; then
|
||||
echo_error "未检测到框架进程"
|
||||
exit 1
|
||||
fi
|
||||
fswatch $watch_dir | while read file
|
||||
do
|
||||
echo "Detect file change: $file"
|
||||
kill -USR1 $_pid
|
||||
done
|
||||
@@ -1,48 +1,62 @@
|
||||
{
|
||||
"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.3",
|
||||
"extra": {
|
||||
"exclude_annotate": [
|
||||
"src/ZM"
|
||||
]
|
||||
"zm": {
|
||||
"exclude-annotation-path": [
|
||||
"src/ZM"
|
||||
]
|
||||
},
|
||||
"hooks": {
|
||||
"post-merge": "composer install",
|
||||
"pre-commit": [
|
||||
"echo committing as $(git config user.name)",
|
||||
"./vendor/bin/php-cs-fixer fix --dry-run --diff ./src"
|
||||
],
|
||||
"pre-push": [
|
||||
"./vendor/bin/php-cs-fixer fix --dry-run --diff ./src",
|
||||
"composer analyse"
|
||||
]
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "whale",
|
||||
"email": "crazysnowcc@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "swift",
|
||||
"email": "hugo_swift@yahoo.com"
|
||||
"name": "jerry",
|
||||
"email": "admin@zhamao.me"
|
||||
}
|
||||
],
|
||||
"prefer-stable": true,
|
||||
"bin": [
|
||||
"bin/start",
|
||||
"bin/phpunit-swoole"
|
||||
"bin/phpunit-swoole",
|
||||
"bin/watcher"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"doctrine/annotations": "~1.10",
|
||||
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0 || ^8.1",
|
||||
"ext-json": "*",
|
||||
"psy/psysh": "@stable",
|
||||
"symfony/polyfill-ctype": "^1.20",
|
||||
"symfony/polyfill-mbstring": "^1.20",
|
||||
"symfony/console": "^5.1",
|
||||
"zhamao/connection-manager": "*@dev",
|
||||
"zhamao/console": "^1.0",
|
||||
"ext-posix": "*",
|
||||
"doctrine/dbal": "^2.13.1",
|
||||
"jelix/version": "^2.0",
|
||||
"koriym/attributes": "^1.0",
|
||||
"psy/psysh": "^0.11.2",
|
||||
"symfony/console": "~5.0 || ~4.0 || ~3.0",
|
||||
"symfony/polyfill-ctype": "^1.19",
|
||||
"symfony/polyfill-mbstring": "^1.19",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/routing": "~5.0 || ~4.0 || ~3.0",
|
||||
"zhamao/config": "^1.0",
|
||||
"zhamao/request": "*@dev",
|
||||
"symfony/routing": "^5.1",
|
||||
"symfony/polyfill-php80": "^1.20",
|
||||
"ext-posix": "*"
|
||||
"zhamao/connection-manager": "^1.0",
|
||||
"zhamao/console": "^1.0",
|
||||
"zhamao/request": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "*",
|
||||
"ext-mbstring": "*"
|
||||
"ext-ctype": "Use C/C++ extension instead of polyfill will be more efficient",
|
||||
"ext-mbstring": "Use C/C++ extension instead of polyfill will be more efficient",
|
||||
"ext-pdo_mysql": "If you use mysql in framework, you will need this extension",
|
||||
"ext-redis": "If you use Redis in framework, you will need this extension",
|
||||
"league/climate": "Display columns and status in terminal"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -52,7 +66,28 @@
|
||||
"src/ZM/global_functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Module\\": "src/Module",
|
||||
"Custom\\": "src/Custom"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "@dev"
|
||||
"brainmaestro/composer-git-hooks": "^2.8",
|
||||
"friendsofphp/php-cs-fixer": "^3.2 != 3.7.0",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||
"swoole/ide-helper": "^4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"[ $COMPOSER_DEV_MODE -eq 0 ] || vendor/bin/cghooks add"
|
||||
],
|
||||
"analyse": "phpstan analyse --memory-limit 300M -l 0 ./src",
|
||||
"cs-fix": "php-cs-fixer fix $1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,123 +1,152 @@
|
||||
<?php
|
||||
/** @noinspection PhpFullyQualifiedNameUsageInspection */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
global $config;
|
||||
|
||||
/** bind host */
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/* bind host */
|
||||
$config['host'] = '0.0.0.0';
|
||||
|
||||
/** bind port */
|
||||
/* bind port */
|
||||
$config['port'] = 20001;
|
||||
|
||||
/** 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||
$config['http_reverse_link'] = "http://127.0.0.1:" . $config['port'];
|
||||
/* 框架开到公网或外部的HTTP访问链接,通过 DataProvider::getFrameworkLink() 获取 */
|
||||
$config['http_reverse_link'] = 'http://127.0.0.1:' . $config['port'];
|
||||
|
||||
/** 框架是否启动debug模式 */
|
||||
/* 框架是否启动debug模式,当debug模式为true时,启用热更新(需要安装inotify扩展) */
|
||||
$config['debug_mode'] = false;
|
||||
|
||||
/** 存放框架内文件数据的目录 */
|
||||
$config['zm_data'] = realpath(__DIR__ . "/../") . '/zm_data/';
|
||||
/* 存放框架内文件数据的目录 */
|
||||
$config['zm_data'] = realpath(WORKING_DIR) . '/zm_data/';
|
||||
|
||||
/** 存放各个模块配置文件的目录 */
|
||||
/* 存放各个模块配置文件的目录 */
|
||||
$config['config_dir'] = $config['zm_data'] . 'config/';
|
||||
|
||||
/** 存放崩溃和运行日志的目录 */
|
||||
/* 存放崩溃和运行日志的目录 */
|
||||
$config['crash_dir'] = $config['zm_data'] . 'crash/';
|
||||
|
||||
/** 对应swoole的server->set参数 */
|
||||
/* 对应swoole的server->set参数 */
|
||||
$config['swoole'] = [
|
||||
'log_file' => $config['crash_dir'] . 'swoole_error.log',
|
||||
'worker_num' => swoole_cpu_num(), //如果你只有一个 OneBot 实例连接到框架并且代码没有复杂的CPU密集计算,则可把这里改为1使用全局变量
|
||||
'dispatch_mode' => 2, //包分配原则,见 https://wiki.swoole.com/#/server/setting?id=dispatch_mode
|
||||
// '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,
|
||||
//'task_enable_coroutine' => true
|
||||
'max_wait_time' => 5,
|
||||
// 'task_worker_num' => 4,
|
||||
// 'task_enable_coroutine' => true
|
||||
];
|
||||
|
||||
/** 轻量字符串缓存,默认开启 */
|
||||
$config['light_cache'] = [
|
||||
'size' => 512, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, //单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, //Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => $config['zm_data'].'_cache.json',
|
||||
'auto_save_interval' => 900
|
||||
];
|
||||
|
||||
/** 大容量跨进程变量存储(2.2.0可用) */
|
||||
$config["worker_cache"] = [
|
||||
"worker" => 0,
|
||||
"transaction_timeout" => 30000
|
||||
];
|
||||
|
||||
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||
$config['sql_config'] = [
|
||||
'sql_host' => '',
|
||||
'sql_port' => 3306,
|
||||
'sql_username' => 'name',
|
||||
'sql_database' => 'db_name',
|
||||
'sql_password' => '',
|
||||
'sql_options' => [
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_EMULATE_PREPARES => false
|
||||
/* 一些框架与框架运行时设置的调整 */
|
||||
$config['runtime'] = [
|
||||
'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL),
|
||||
'swoole_server_mode' => SWOOLE_PROCESS,
|
||||
'middleware_error_policy' => 1,
|
||||
'reload_delay_time' => 800,
|
||||
'global_middleware_binding' => [],
|
||||
'save_console_log_file' => false, // 改为目标路径,则将 Console 输出的日志保存到文件
|
||||
'annotation_reader_ignore' => [ // 设置注解解析器忽略的注解名或命名空间,防止解析到不该解析的
|
||||
'name' => [
|
||||
'mixin',
|
||||
],
|
||||
'namespace' => [],
|
||||
],
|
||||
'sql_no_exception' => false,
|
||||
'sql_default_fetch_mode' => PDO::FETCH_ASSOC // added in 1.5.6
|
||||
];
|
||||
|
||||
/** Redis连接信息,host留空则启动时不创建Redis连接池 */
|
||||
/* 轻量字符串缓存,默认开启 */
|
||||
$config['light_cache'] = [
|
||||
'size' => 512, // 最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, // 单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, // Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => $config['zm_data'] . '_cache.json',
|
||||
'auto_save_interval' => 900,
|
||||
];
|
||||
|
||||
/* 大容量跨进程变量存储(2.2.0可用) */
|
||||
$config['worker_cache'] = [
|
||||
'worker' => 0,
|
||||
'transaction_timeout' => 30000,
|
||||
];
|
||||
|
||||
/* MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||
$config['mysql_config'] = [
|
||||
'host' => '',
|
||||
'port' => 3306,
|
||||
'unix_socket' => null,
|
||||
'username' => 'root',
|
||||
'password' => '123456',
|
||||
'dbname' => '',
|
||||
'charset' => 'utf8mb4',
|
||||
'pool_size' => 64,
|
||||
'options' => [
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
],
|
||||
];
|
||||
|
||||
/* Redis连接信息,host留空则启动时不创建Redis连接池 */
|
||||
$config['redis_config'] = [
|
||||
'host' => '',
|
||||
'port' => 6379,
|
||||
'timeout' => 1,
|
||||
'db_index' => 0,
|
||||
'auth' => ''
|
||||
'auth' => '',
|
||||
];
|
||||
|
||||
/** onebot连接约定的token */
|
||||
$config["access_token"] = '';
|
||||
/* onebot连接约定的token */
|
||||
$config['access_token'] = '';
|
||||
|
||||
/** HTTP服务器固定请求头的返回 */
|
||||
/* HTTP服务器固定请求头的返回 */
|
||||
$config['http_header'] = [
|
||||
'Server' => 'zhamao-framework',
|
||||
'Content-Type' => 'text/html; charset=utf-8'
|
||||
'Content-Type' => 'text/html; charset=utf-8',
|
||||
];
|
||||
|
||||
/** HTTP服务器在指定状态码下回复的页面(默认) */
|
||||
/* HTTP服务器在指定状态码下回复的页面(默认) */
|
||||
$config['http_default_code_page'] = [
|
||||
'404' => '404.html'
|
||||
'404' => '404.html',
|
||||
];
|
||||
|
||||
/** zhamao-framework在框架启动时初始化的atomic们 */
|
||||
/* zhamao-framework在框架启动时初始化的atomic们 */
|
||||
$config['init_atomics'] = [
|
||||
//'custom_atomic_name' => 0, //自定义添加的Atomic
|
||||
// 'custom_atomic_name' => 0, //自定义添加的Atomic
|
||||
];
|
||||
|
||||
/** 终端日志显示等级(0-4) */
|
||||
$config["info_level"] = 2;
|
||||
/* 终端日志显示等级(0-4) */
|
||||
$config['info_level'] = 2;
|
||||
|
||||
/** 上下文接口类 implemented from ContextInterface */
|
||||
/* 上下文接口类 implemented from ContextInterface */
|
||||
$config['context_class'] = \ZM\Context\Context::class;
|
||||
|
||||
/** 静态文件访问 */
|
||||
/* 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => false,
|
||||
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||
'document_root' => realpath(__DIR__ . '/../') . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html'
|
||||
]
|
||||
'index.html',
|
||||
],
|
||||
];
|
||||
|
||||
/** 注册 Swoole Server 事件注解的类列表 */
|
||||
$config['server_event_handler_class'] = [
|
||||
\ZM\Event\ServerEventHandler::class,
|
||||
/* 机器人解析模块,关闭后无法使用如CQCommand等注解(上面的modules即将废弃) */
|
||||
$config['onebot'] = [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false,
|
||||
'message_level' => 99,
|
||||
'message_convert_string' => true,
|
||||
'message_command_policy' => 'interrupt',
|
||||
];
|
||||
|
||||
/** 服务器启用的外部第三方和内部插件 */
|
||||
$config['modules'] = [
|
||||
'onebot' => [
|
||||
'status' => true,
|
||||
'single_bot_mode' => false
|
||||
], // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
|
||||
/* 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */
|
||||
$config['remote_terminal'] = [
|
||||
'status' => false,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 20002,
|
||||
'token' => '',
|
||||
];
|
||||
|
||||
/* 模块(插件)加载器的相关设置 */
|
||||
$config['module_loader'] = [
|
||||
'enable_hotload' => false,
|
||||
'load_path' => $config['zm_data'] . 'modules',
|
||||
];
|
||||
|
||||
return $config;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# FAQ
|
||||
@@ -85,17 +85,20 @@ bin/start server # 通过源码模式启动框架
|
||||
- `--debug-mode`:启用调试模式,调试模式的作用是关闭一键协程化和终端交互,减少 Swoole 本身对代码逻辑的干扰(比如执行 `shell_exec()` 报错的话可以开启这个进行调试)。
|
||||
- `--log-{mode}`:设置 log 等级。支持 `--log-debug`,`--log-verbose`,`--log-info`,`--log-warning`,`--log-error`。
|
||||
- `--log-theme`:设置终端信息的主题。这个选项适用于多种终端信息显示的兼容,例如白色终端和不支持颜色的终端。详见 [Console - 主题设置](/component/console/#_2)。
|
||||
- `--disable-console-input`:关闭终端交互,如果你使用的不是 tmux、screen 而是直接将进程使用 systemd 等方式运行到 init 守护进程下,则需要关闭终端交互输入,关闭后不可以使用 `stop, reload, logtest` 等交互命令。
|
||||
- `--disable-coroutine`:关闭一键协程化。
|
||||
- `--remote-terminal`:开启 nc 远程终端,配置文件使用全局中的 `remote_terminal` 项。也可以在全局配置中常开启(status 设置为 true)。
|
||||
- `--daemon`:以守护进程方式运行框架,此参数将直接在输出 motd 后将进程挂到 init 下运行,后台常驻。
|
||||
- `--watch`:监控 `src/` 目录下的文件变化,有变化则自动重新载入代码。开启监控需要安装 PHP 扩展:inotify。使用 pecl 就可以安装:`pecl install inotify`。
|
||||
- `--watch`:监控 `src/` 目录下的文件变化,有变化则自动重新载入代码。开启监控需要安装 PHP 扩展:inotify。使用 pecl 就可以安装:`pecl install inotify`。(注:不支持 WSL 和 macOS)
|
||||
- `--env`:设置运行环境,设置运行环境后将优先加载指定环境的配置文件,支持 `--env=production`,`--env=staging`,`--env=development`,见 [基本配置](/guide/basic-config/#_2)。
|
||||
- `--worker-num`:指定运行的工作进程数量(并不是越多越好,框架默认为 CPU 核心数),例如 `--worker-num=8`。
|
||||
- `--task-worker-num`:启用 TaskWorker 进程并指定数量。
|
||||
- `--show-php-ver`:在启动时显示 Swoole 和 PHP 的版本。
|
||||
|
||||
## 守护进程操作命令
|
||||
|
||||
守护进程在 2.2.0 版本开始,可以使用命令行快速操作,如重启、停止、查看状态等。
|
||||
|
||||
注意,这里的守护进程操作命令是指 **使用 `--daemon` 方式启动的框架**,如使用 Docker、screen、tmux 等方式挂后台跑则此命令不可用!
|
||||
注意,这里的守护进程操作命令是指 **使用 `--daemon` 方式启动的框架**,如使用 Docker、screen、tmux、systemd 等方式挂后台跑则此命令不可用!
|
||||
|
||||
```bash
|
||||
vendor/bin/start daemon:status # 查看守护进程的状态
|
||||
@@ -116,4 +119,16 @@ vendor/bin/start simple-http-server your-web-dir/ --host=0.0.0.0 --port=8080
|
||||
```
|
||||
|
||||
- `your-web-dir` 是必填的参数。
|
||||
- `--host` 和 `--port` 是可选参数,如果不填,则默认使用 `global.php` 配置文件中的配置。
|
||||
- `--host` 和 `--port` 是可选参数,如果不填,则默认使用 `global.php` 配置文件中的配置。
|
||||
|
||||
### 检查配置是否更新
|
||||
|
||||
默认情况下(非源码模式),你可以使用命令 `vendor/bin/start check:config` 来检查你的配置文件是否需要更新部分段落。
|
||||
|
||||
### systemd 配置文件生成器
|
||||
|
||||
框架支持生成 systemd 配置文件 `zhamao.service`,生成后将文件放入 `/etc/systemd/system` 后输入 `systemctl enable zhamao.service` 即可。
|
||||
|
||||
命令:`vendor/bin/start systemd:generate`
|
||||
|
||||
注意,systemd 启动的守护进程模式和使用参数 `--daemon` 不一样,请勿同时混用,直接使用上述命令生成的配置文件即可正常使用!
|
||||
231
docs/advanced/example/admin.md
Normal file
231
docs/advanced/example/admin.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# 编写管理员专属功能
|
||||
|
||||
众所周知,如果大家使用炸毛框架来开发聊天机器人的话,会比较方便。但是有些地方你一定会感觉还是欠缺了点,比如下面这样,你想编写一个只能由机器人管理员,也就是你自己,才能触发的功能:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand(match="禁言",message_type="group")
|
||||
*/
|
||||
public function banSomeone() {
|
||||
$r1 = ctx()->getNextArg("请输入禁言的人或at他");
|
||||
$r2 = ctx()->getFullArg("请输入禁言的时间(秒)");
|
||||
$cq = CQ::getCQ($r1);
|
||||
if ($cq !== null) {
|
||||
if ($cq["type"] != "at") return "请at或者输入正确的QQ号!";
|
||||
$r1 = $cq["params"]["qq"];
|
||||
}
|
||||
// 群内禁言用户
|
||||
ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, $r2);
|
||||
return "禁言成功!";
|
||||
}
|
||||
```
|
||||
|
||||
这时候,如果只是自己有绝对的权利,可以将自己的 QQ 号写死在注解 `@CQCommand` 中,并限定 `user_id`(假设我的 QQ 号码为 123456):
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand(match="禁言",message_type="group",user_id=123456)
|
||||
*/
|
||||
```
|
||||
|
||||
但是,随着时间的推移,你的机器人伙伴群可能越来越大,这个命令可能不止需要绝对的你来使用,你还要将机器人的部分权利下发给更多的伙伴,怎么办呢?注解里面只能写死的。
|
||||
|
||||
答案很简单,这时候我们就需要用到框架提供的中间件(Middleware)。中间件说白了就是在事件执行前、后、过程中抛出的异常对其进行阻断和插入代码,比如我们上方在触发禁言这个注解事件前首先要判断执行这个命令的是不是钦定的管理员。
|
||||
|
||||
## 第一步:定义中间件
|
||||
|
||||
首先,我们需要定义一个中间件。在框架默认提供的脚手架中,包含了一个叫 `TimerMiddleware.php` 的示例中间件,这个示例中间件的目的是非常简单的,就是判断这个注解事件运行了多长时间。假设你有一个机器人功能,这个功能下的代码需要执行很长时间,可以使用这一注解轻松将事件执行的时间打印到终端上。
|
||||
|
||||
关于中间件的有关说明,见 [中间件](/event/middleware)。
|
||||
|
||||
下面我们假设你已经阅读过中间件注解的文档了,我们着手编写一个判断指令执行者是否是指定的管理员 QQ 的中间件。为了省事和让大家方便地复现,我先在脚手架下的目录 `src/Module/Middleware/` 下新建 PHP 类文件 `AdminMiddleware.php`(和 `TimerMiddleware.php` 在同一个目录)。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Module\Middleware;
|
||||
|
||||
use ZM\Annotation\Http\HandleBefore;
|
||||
use ZM\Annotation\Http\MiddlewareClass;
|
||||
use ZM\Exception\ZMException;
|
||||
use ZM\Http\MiddlewareInterface;
|
||||
use ZM\Store\LightCache;
|
||||
|
||||
/**
|
||||
* Class AdminMiddleware
|
||||
* 示例中间件:用于动态管理一些管理员指令的中间件
|
||||
* @package Module\Middleware
|
||||
* @MiddlewareClass("admin")
|
||||
*/
|
||||
class AdminMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @HandleBefore()
|
||||
* @return bool
|
||||
* @throws ZMException
|
||||
*/
|
||||
public function onBefore(): bool {
|
||||
$r = ctx()->getUserId(); // 从上下文获取发消息的用户 QQ
|
||||
$admin_list = LightCache::get("admin_list") ?? []; // 从轻量缓存获取管理员列表
|
||||
return in_array($r, $admin_list); // 返回这个 QQ 是否在管理员列表中
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中,`@MiddlewareClass("admin")` 的意思是,定义这个类为名字叫 `admin` 的中间件,同时,所有中间件的类**必须**带上 `implements MiddlewareInterface`,统一接口形式。
|
||||
|
||||
`@HandleBefore()` 代表的是,这个类下的这个函数(onBefore)被标注为这个中间件的 `onBefore` 事件,也就是说,如果有别的注解事件插入了这个 `admin` 中间件,那么执行对应注解事件前都要执行一下 `@HandleBefore` 所绑定的这个函数。而这个绑定的函数只能返回 `bool` 类型的值哦!
|
||||
|
||||
## 第二步:使用中间件
|
||||
|
||||
使用中间件很简单,在需要阻断的注解事件绑定的函数上再加一个注解就好了!我们以上方的禁言例子说明:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @Middleware("admin")
|
||||
* @CQCommand(match="禁言",message_type="group")
|
||||
*/
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
^ 假设我是管理员
|
||||
) 禁言 1234567 600
|
||||
( 禁言成功!
|
||||
^ 假设我不在管理员名单里
|
||||
) 禁言 1234567 900
|
||||
^ 机器人没有回复,因为中间件返回了 false,不继续执行
|
||||
</chat-box>
|
||||
|
||||
而这时候有朋友又要问了,如果我有一系列管理员命令,假设都在一个叫 `AdminFunc.php` 的模块类里,我是不是还得一个一个地给注解事件写 `@Middleware("admin")` 呢?当然不需要!如果你这个类所有的注解事件都是机器人的聊天事件(`@CQCommand`,`@CQMessage`)的话,可以直接给类注解这个中间件,效果等同于给每一个函数写一次中间件注解。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\Http\Middleware;
|
||||
|
||||
/**
|
||||
* Class AdminFunc
|
||||
* @package Module\Example
|
||||
* @Middleware("admin")
|
||||
*/
|
||||
class AdminFunc
|
||||
{
|
||||
// ...这里是你的一堆注解事件的函数
|
||||
}
|
||||
```
|
||||
|
||||
## 第三步:补全代码
|
||||
|
||||
上面我们讲到了,中间件里面使用了 `LightCache` 轻量缓存来储存临时的管理员列表,那么我们将这部分的代码完善吧!
|
||||
|
||||
=== "src/Module/Example/AdminFunc.php"
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Http\Middleware;
|
||||
use ZM\API\CQ;
|
||||
|
||||
/**
|
||||
* Class AdminFunc
|
||||
* @package Module\Example
|
||||
* @Middleware("admin")
|
||||
*/
|
||||
class AdminFunc
|
||||
{
|
||||
/**
|
||||
* @CQCommand(match="禁言",message_type="group")
|
||||
*/
|
||||
public function banSomeone() {
|
||||
$r1 = ctx()->getNextArg("请输入禁言的人或at他");
|
||||
$r2 = ctx()->getFullArg("请输入禁言的时间(秒)");
|
||||
$cq = CQ::getCQ($r1);
|
||||
if ($cq !== null) {
|
||||
if ($cq["type"] != "at") return "请at或者输入正确的QQ号!";
|
||||
$r1 = $cq["params"]["qq"];
|
||||
}
|
||||
// 群内禁言用户
|
||||
ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, $r2);
|
||||
return "禁言成功!";
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQCommand(match="解除禁言",message_type="group")
|
||||
*/
|
||||
public function unbanSomeone() {
|
||||
$r1 = ctx()->getNextArg("请输入禁言的人或at他");
|
||||
$cq = CQ::getCQ($r1);
|
||||
if ($cq !== null) {
|
||||
if ($cq["type"] != "at") return "请at或者输入正确的QQ号!";
|
||||
$r1 = $cq["params"]["qq"];
|
||||
}
|
||||
// 群内禁言用户
|
||||
ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, 0);
|
||||
return "解除禁言成功!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== "src/Module/Example/AdminManager.php"
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Http\Middleware;
|
||||
use ZM\Annotation\Swoole\OnStart;
|
||||
use ZM\Store\LightCache;
|
||||
use ZM\Store\Lock\SpinLock;
|
||||
|
||||
class AdminManager
|
||||
{
|
||||
/**
|
||||
* @OnStart()
|
||||
*/
|
||||
public function onStart() {
|
||||
if (!LightCache::isset("admin_list")) { //一次性代码,首次执行才会执行if
|
||||
LightCache::set("admin_list", [ // 框架启动时初始化管理员列表
|
||||
"123456",
|
||||
"234567"
|
||||
], -2); // 这里用 -2 的原因是将这一列表持久化保存,避免关闭框架后丢失
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQCommand(match="添加管理员")
|
||||
* @Middleware("admin")
|
||||
*/
|
||||
public function addAdmin() { //只有管理员才能添加管理员
|
||||
$qq = ctx()->getNextArg("请输入要添加管理员的QQ(qq号码,不可at)");
|
||||
SpinLock::lock("admin_list"); //如果是多进程模式的话需要加锁
|
||||
$ls = LightCache::get("admin_list");
|
||||
if (!in_array($qq, $ls)) $ls[] = $qq;
|
||||
LightCache::set("admin_list", $ls, -2);
|
||||
SpinLock::unlock("admin_list"); //如果是多进程模式的话需要加锁
|
||||
return "成功添加 $qq 到管理员列表!";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
^ 现在我是 123456
|
||||
) 禁言 13579 60
|
||||
( 禁言成功!
|
||||
) 解除禁言 13579
|
||||
( 解除禁言成功!
|
||||
) 添加管理员 98765
|
||||
( 成功添加 98765 到管理员列表!
|
||||
^ 现在我是98765
|
||||
) 禁言 13579
|
||||
( 请输入禁言的时间(秒)
|
||||
) 120
|
||||
( 禁言成功!
|
||||
</chat-box>
|
||||
|
||||
88
docs/advanced/example/integrate-qingyunke-chatbot.md
Normal file
88
docs/advanced/example/integrate-qingyunke-chatbot.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 接入青云客智能聊天机器人API
|
||||
|
||||
作为一个群聊机器人,懂得聊天会让机器人增色不少,在大数据和 AI 热潮下,不少厂商都研发了自己的智能聊天 API,例如图灵机器人、腾讯智能闲聊等,大厂开发的 API 自然有着他人无可比拟的健壮性和可靠性,但是随之而来不菲的价格显然并不适合大众开发者。这时候一个免费、可用的智能聊天 API 便非常重要了,其中,青云客是少有的完全免费、无需注册的智能聊天 API,提供了包括智能聊天、歌词、天气查询、笑话等多种有用功能,且接入简单,非常适合新手开发者尝试。
|
||||
|
||||
## 结果演示
|
||||

|
||||
|
||||
|
||||
## 阅读接入指南
|
||||
|
||||
不管接入何种服务,阅读接入指南永远都是最优先、最重要的一步,所幸青云客的接入指南十分简单,简单来说归纳为以下:
|
||||
* 请求:GET https://api.qingyunke.com/api.php
|
||||
* 参数:
|
||||
* * `key` 目前固定为 `free`
|
||||
* * `appid` 目前固定为 `0`
|
||||
* * `msg` 关键词,需要经过 `urlencode`
|
||||
* 注意:返回结果中 `{br}` 代表换行
|
||||
|
||||
## 逻辑编写
|
||||
|
||||
阅读过后,我们便可以进行主要的编写工作了。
|
||||
|
||||
首先,为了机器人的性能考虑,也为了避免过分打扰群聊的聊天,我们希望机器人只有在主动触发(@AT 或者 关键词等)时才会进行智能聊天。
|
||||
|
||||
对于关键词匹配,我们可以使用 `@CQCommand`:
|
||||
|
||||
```php
|
||||
/**
|
||||
* 智能聊天
|
||||
*
|
||||
* @CQCommand(start_with="机器人")
|
||||
*/
|
||||
public function chat()
|
||||
{
|
||||
// 替换掉机器人前缀,并获取消息内容
|
||||
$msg = ctx()->getMessage();
|
||||
$msg = str_replace('机器人', '', $msg);
|
||||
if (empty(trim($msg))) {
|
||||
$msg = ctx()->getFullArg('怎么了?');
|
||||
}
|
||||
|
||||
Console::info('正在获取智能聊天回复:' . $msg);
|
||||
// 请求 API 获取回复
|
||||
$raw_data = file_get_contents('https://api.qingyunke.com/api.php?key=free&appid=0&msg=' . urlencode($msg));
|
||||
try {
|
||||
$data = json_decode($raw_data, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\Exception $e) {
|
||||
$data = ['content' => '机器人解析异常,请稍后再试'];
|
||||
Console::warning('无法获取智能聊天回复:' . $e->getMessage());
|
||||
}
|
||||
if ($data['result'] !== 0) {
|
||||
$data = ['content' => '机器人服务异常,请稍后再试'];
|
||||
Console::warning('无法获取智能聊天回复:' . $raw_data);
|
||||
}
|
||||
Console::info('获取智能聊天回复完成:' . $data['content']);
|
||||
// 将 {br} 替换为换行
|
||||
$data['content'] = strtr($data['content'], ['{br}' => "\n"]);
|
||||
return $data['content'];
|
||||
}
|
||||
```
|
||||
|
||||
这样我们的命令便只会在用户发送以`机器人`开头的消息时才会触发。
|
||||
|
||||
同时,我们也希望在 @AT 机器人时也进行回复,此时可以使用 `@CQBefore` 方法进行折中:
|
||||
|
||||
```php
|
||||
/**
|
||||
* 将 AT 机器人的消息交由智能聊天处理
|
||||
*
|
||||
* @CQBefore("message")
|
||||
*/
|
||||
public function changeAt(): bool
|
||||
{
|
||||
// 判断此条消息是否 AT 了机器人
|
||||
if (MessageUtil::isAtMe(ctx()->getMessage(), ctx()->getRobotId())) {
|
||||
// 将 AT 本身从消息中去掉
|
||||
$msg = str_replace(CQ::at(ctx()->getRobotId()), '', ctx()->getMessage());
|
||||
ctx()->setMessage('机器人' . trim($msg));
|
||||
// 调用智能聊天
|
||||
ctx()->reply($this->chat());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
112
docs/advanced/example/weather-bot.md
Normal file
112
docs/advanced/example/weather-bot.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# 基于词性分析和魅族天气的天气查询机器人
|
||||
|
||||
本文将基于 [`jieba-php`](https://github.com/fukuball/jieba-php) 中文分词库以及 [魅族天气 API](https://github.com/shichunlei/-Api/blob/master/MeizuWeather.md) 开发一个天气查询机器人。
|
||||
|
||||
## 结果演示
|
||||

|
||||
尾部的随机表情并非本教程的一部分。
|
||||
|
||||
## 逻辑编写
|
||||
|
||||
[`jieba-php`](https://github.com/fukuball/jieba-php) 是目前比较好用的中文分词库,虽然最近的维护并不活跃,但已足够我们的需求:
|
||||
```shell
|
||||
composer require fukuball/jieba-php:dev-master
|
||||
```
|
||||
|
||||
以下代码使用了本文作者自行编写的天气查询库,需要进行引入:
|
||||
```shell
|
||||
composer require sunxyw/weather
|
||||
```
|
||||
您也可以将以下代码自行改写为直接调用魅族天气 API,详情请参阅[魅族天气 API 文档](https://github.com/shichunlei/-Api/blob/master/MeizuWeather.md)。
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Bot\Module\SmartChat;
|
||||
|
||||
use Fukuball\Jieba\Jieba;
|
||||
use Fukuball\Jieba\Posseg;
|
||||
use Sunxyw\Weather\Weather;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Console\Console;
|
||||
|
||||
class WeatherReport
|
||||
{
|
||||
/**
|
||||
* 加载字典
|
||||
*
|
||||
* @OnStart(worker_id=-1)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initDictionary(): void
|
||||
{
|
||||
// 分词以及词性分析需要载入字典到内存
|
||||
ini_set('memory_limit', '600M');
|
||||
Jieba::init(['dict' => 'small']);
|
||||
Posseg::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询天气
|
||||
*
|
||||
* @CQCommand(keyword="天气")
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function cmdQueryWeather(): string
|
||||
{
|
||||
// 分词并进行词性分析
|
||||
$seg_list = Posseg::cut(ctx()->getMessage());
|
||||
$tags = array_column($seg_list, 'tag');
|
||||
// 找出词性为 ns(地名)的单词
|
||||
$location_index = array_search('ns', $tags, true);
|
||||
$location = $seg_list[$location_index]['word'];
|
||||
|
||||
// 此处引入了本文作者自己写的天气库
|
||||
$w = new Weather();
|
||||
try {
|
||||
$report = $w->getWeather($location);
|
||||
} catch (\InvalidArgumentException) {
|
||||
return '城市输入错误';
|
||||
} catch (\JsonException $e) {
|
||||
Console::warning("天气查询失败:{$e->getMessage()}");
|
||||
return '天气查询失败';
|
||||
}
|
||||
|
||||
$template = <<<EOF
|
||||
%s天气:%s
|
||||
温度:%s℃
|
||||
湿度:%s%%
|
||||
风向:%s %s
|
||||
空气质量:%s
|
||||
------------------------------
|
||||
未来三天天气:
|
||||
%s:%s,日间%s℃,夜间%s℃,吹%s %s
|
||||
%s:%s,日间%s℃,夜间%s℃,吹%s %s
|
||||
%s:%s,日间%s℃,夜间%s℃,吹%s %s
|
||||
EOF;
|
||||
$args = [
|
||||
$report->getCity(),
|
||||
$report->getRealtime()['weather'],
|
||||
$report->getRealtime()['temperature'],
|
||||
$report->getRealtime()['humidity'],
|
||||
$report->getRealtime()['wind_direction'],
|
||||
$report->getRealtime()['wind_speed'],
|
||||
$report->getRealtime()['air_quality'],
|
||||
];
|
||||
foreach (array_slice($report->getForecastDaily(), 0, 3) as $forecast) {
|
||||
$args[] = $forecast['date'];
|
||||
$args[] = $forecast['weather'];
|
||||
$args[] = $forecast['temperature']['day'];
|
||||
$args[] = $forecast['temperature']['night'];
|
||||
$args[] = $forecast['wind_direction'];
|
||||
$args[] = $forecast['wind_speed'];
|
||||
}
|
||||
return vsprintf($template, ...$args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
## 框架运行总结构图
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
@@ -27,3 +27,44 @@
|
||||
TODO:先放一放。
|
||||
```
|
||||
|
||||
## ZM\Entity\MatchObject
|
||||
|
||||
此类是调用方法 `MessageUtil::matchCommand()` 返回的对象体,含有匹配成功与否和匹配到的注解相关的信息。
|
||||
|
||||
### 属性
|
||||
|
||||
- `$match`:`bool` 类型,返回匹配是否成功
|
||||
- `$object`:`CQCommand` 注解类,如果匹配成功则返回对应的 `@CQCommand` 信息
|
||||
- `match`:`array` 类型,如果匹配成功则返回匹配到的参数
|
||||
|
||||
```php
|
||||
// 假设我有一个注解事件 @CQCommand(match="你好"),绑定的函数是 \Module\Example\Hello 下的 hello123()
|
||||
|
||||
$obj = MessageUtil::matchCommand("你好 我叫顺溜 我今年二十八", ctx()->getData());
|
||||
/* 以下是返回信息,仅供参考
|
||||
$obj->match ==> true
|
||||
$obj->object ==> \ZM\Annotation\CQ\CQCommand: (
|
||||
match: "你好",
|
||||
pattern: "",
|
||||
regex: "",
|
||||
start_with: "",
|
||||
end_with: "",
|
||||
keyword: "",
|
||||
alias: [],
|
||||
message_type: "",
|
||||
user_id: 0,
|
||||
group_id: 0,
|
||||
discuss_id: 0,
|
||||
level: 20,
|
||||
method: "hello123",
|
||||
class: \Module\Example\Hello::class
|
||||
)
|
||||
$obj->match ==> [
|
||||
"我叫顺溜",
|
||||
"我今年二十八"
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
3
docs/advanced/manually-install.md
Normal file
3
docs/advanced/manually-install.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 手动部署环境教程
|
||||
|
||||
TODO: 还没写
|
||||
70
docs/advanced/multi-process.md
Normal file
70
docs/advanced/multi-process.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# 框架多进程
|
||||
|
||||
首先对于多进程概念,对于传统 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 构建,所以天然支持协程,而协程就是针对 I/O 操作进行一个调度,类似异步的 Node.js,所以针对项目中存在太多的 SQL 语句执行、文件读写的话,炸毛框架直接上手,无需做任何修改,也可以达到很好的性能。
|
||||
|
||||
**但是**,CPU 密集型的应用怎么办呢?假设我的 Web 应用有大量的排序、md5 运算怎么办呢?这样的阻塞,假设是一个超级大的 for 循环或者是要执行很长时间的 while 循环,CPU 一直在被占用。多进程就是针对 CPU 密集型的应用说 yes 的一个方案。
|
||||
|
||||

|
||||
|
||||
我们假设现在有 3 个请求同时访问,也就是说上面的流程需要执行 3 遍。而如果我们只有一个进程的话,最后一个请求需要等待的时间为 `2*3+5*3=21` 秒,非常耗时。
|
||||
|
||||
而如果有两个进程处理 3 个请求,则最后一个完成的请求就缩短了,`2+5+2+5=14` 秒。
|
||||
|
||||

|
||||
|
||||
所以如果要充分利用你的服务器或者个人电脑的多核 CPU 资源,就要设置多个进程来处理。一个进程只能在一个 CPU 上运行,而设置了多进程后,就可以让多核 CPU 充分运行多个进程,所以我们给框架设置多进程的推荐数值为等同于 CPU 的核心数。
|
||||
|
||||
## 为什么不是多线程
|
||||
|
||||
因为众所周知,PHP 对线程的支持比较不好,而 ZTS 版本的 PHP 又会影响传统的 Web 端 PHP 的性能,再加上 Linux 对线程的切换效率和多进程切换的效率差不多,多线程容易造成数据读写不安全等问题,故 Swoole 使用的是多进程模型。
|
||||
|
||||
## 框架进程模型
|
||||
|
||||

|
||||
|
||||
上图中,横向的时间片可以理解为并行执行,这些操作在多个 CPU 内可能同时在执行。
|
||||
|
||||
## 进程间隔离
|
||||
|
||||
众所周知,进程是程序在操作系统中的一个边界,和自己有关的一切变量、内容和代码都在自己的进程内,不同进程之间如果不使用管道等方式,是不可以互相访问的。而加上开始描述的,创建子进程是一个复制自身的过程,所以也就会有如下图的情况:
|
||||
|
||||

|
||||
|
||||
我们以静态类为例,设置一个进程中的全局变量。这里就会出现,同一个静态变量在多个进程中完全不同的值的结果。此后,我们将会在 Worker 进程中执行用户的代码,如果设置 Worker 数量仅为 1 的话,那么就简单许多了,你还是可以使用全局变量或静态类来存储你想要的内容而不用担心这种多个进程变量隔离的情况(因为用户的 Web 请求处理的代码只会在一个 Worker 进程中执行)。如果像上图一样设置了多个 Worker,则用户过来的比如 HTTP 请求就有可能出现在不同的 Worker 进程中,给全局变量设值就一定会造成不同步的问题。这时我们就不可以使用全局变量做数据同步(注意,我说的是数据同步)。
|
||||
|
||||
## 跨进程同步
|
||||
|
||||
跨进程同步方案中,框架给出了很多种解决方案。
|
||||
|
||||
- MySQL 数据库
|
||||
- Redis
|
||||
- LightCache 轻量缓存(共享内存)
|
||||
- WorkerCache 大缓存
|
||||
- ZMAtomic 跨进程原子计数器
|
||||
|
||||
下面的表格我将列出下方的特点和各自的优缺点:
|
||||
|
||||
| 类型 | 用途 | 优点 | 缺点 |
|
||||
| ----------- | --------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| MySQL | 大型的传统的关系式数据都可以用数据库,你懂的 | 就是数据库的优点 | 和数据库不在同一台服务器的话网络延迟会较大,数据获取效率不高 |
|
||||
| Redis | 传统的 key-value 数据库 | 数据无同步等问题,性能高 | 有网络通信延迟 |
|
||||
| LightCache | 框架封装的跨进程的 key-value 存储模型 | 性能强悍,无 I/O 和网络通信 | 需要提前分配最大内存大小,最大单个值长度大小,不灵活 |
|
||||
| WorkerCache | 框架封装的基于进程的 key-value 存储模型,类似 Redis | 无需提前分配最大内存大小,受限于 PHP memory_limit | 见 WorkerCache 的说明 |
|
||||
|
||||
!!! note "WorkerCache 的说明"
|
||||
对于 WorkerCache 来说,其实是比较特殊的进程间通信。具体来说就是,WorkerCache 的原理就是将变量指定的存到一个进程中,如果是本进程读写的话直接相当于改一下全局变量,如果是其他进程读写的话,则依靠进程间通信。
|
||||
|
||||
所以缺点也显而易见,如果使用过程中不是命中了 WorkerCache 存储所在的进程的话,则一直会使用进程间通信,影响一定的效率。
|
||||
|
||||
4
docs/advanced/task-worker.md
Normal file
4
docs/advanced/task-worker.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# 使用 TaskWorker 进程处理密集运算
|
||||
|
||||
> 新开个坑,有时间补上。(__填坑标记__)
|
||||
|
||||
1
docs/assets/css/library/default.min.css
vendored
Normal file
1
docs/assets/css/library/default.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.hljs{display:block;overflow-x:auto;padding:.5em;background:#f0f0f0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#bc6060}.hljs-literal{color:#78a960}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB |
59
docs/component/bot/access-token.md
Normal file
59
docs/component/bot/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` 的相关章节。
|
||||
@@ -82,15 +82,20 @@ class Hello {
|
||||
|
||||
CQ 码字符反转义。
|
||||
|
||||
定义:`CQ::encode($msg, $is_content = false)`
|
||||
|
||||
当 `$is_content` 为 true 时,会将 `,` 转义为 `,`。
|
||||
|
||||
| 反转义前 | 反转义后 |
|
||||
| -------- | -------- |
|
||||
| `&` | `&` |
|
||||
| `[` | `[` |
|
||||
| `]` | `]` |
|
||||
| `,` | `,` |
|
||||
|
||||
```php
|
||||
$str = CQ::decode("[我只是一条普通的文本]");
|
||||
// 转换为 "[我只是一条普通的文本]"
|
||||
$str = CQ::decode("[CQ:at,qq=我只是一条普通的文本]");
|
||||
// 转换为 "[CQ:at,qq=我只是一条普通的文本]"
|
||||
```
|
||||
|
||||
### CQ::encode()
|
||||
@@ -102,6 +107,14 @@ $str = CQ::encode("[CQ:我只是一条普通的文本]");
|
||||
// $str: "[CQ:我只是一条普通的文本]"
|
||||
```
|
||||
|
||||
定义:`CQ::encode($msg, $is_content = false)`
|
||||
|
||||
当 `$is_content` 为 true 时,会将 `,` 转义为 `,`。
|
||||
|
||||
### CQ::escape()
|
||||
|
||||
同 `CQ::encode()`。
|
||||
|
||||
### CQ::removeCQ()
|
||||
|
||||
去除字符串中所有的 CQ 码。
|
||||
@@ -111,6 +124,53 @@ $str = CQ::removeCQ("[CQ:at,qq=all]这是带表情的全体消息[CQ:face,id=8]"
|
||||
// $str: "这是带表情的全体消息"
|
||||
```
|
||||
|
||||
### CQ::getCQ()
|
||||
|
||||
解析 CQ 码。
|
||||
|
||||
- 定义:`getCQ($msg, $is_object = false)`
|
||||
- 参数 `$is_object` 为 true 时,返回一个 `\ZM\Entity\CQObject` 对象,此对象的属性和下表相同。(2.3.0+ 版本可用)
|
||||
- 返回:`数组 | CQObject | null`,见下表。
|
||||
|
||||
| 键名 | 说明 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| type | CQ码类型,比如 `[CQ:at]` 中的 `at` |
|
||||
| params | 参数列表,比如 `[CQ:image,file=123.jpg,url=http://a.com/a.jpg]`,params 为 `["file" => "123","url" => "http://a.com/a.jpg"]` |
|
||||
| start | 此 CQ 码在字符串中的起始位置 |
|
||||
| end | 此 CQ 码在字符串中的结束位置 |
|
||||
|
||||
### CQ::getAllCQ()
|
||||
|
||||
定义:`CQ::getAllCQ($msg, $is_object = false)`
|
||||
|
||||
参数 `$is_object` 为 true 时,返回一个 `\ZM\Entity\CQObject[]` 对象数组,此对象的属性和上面的表格内相同。(2.3.0+ 版本可用)
|
||||
|
||||
解析 CQ 码,和 `getCQ()` 的区别是,这个会将字符串中的所有 CQ 码都解析出来,并以同样上方解析出来的数组格式返回。
|
||||
|
||||
```php
|
||||
CQ::getAllCQ("[CQ:at,qq=123]你好啊[CQ:at,qq=456]");
|
||||
/*
|
||||
[
|
||||
[
|
||||
"type" => "at",
|
||||
"params" => [
|
||||
"qq" => "123",
|
||||
],
|
||||
"start" => 0,
|
||||
"end" => 13,
|
||||
],
|
||||
[
|
||||
"type" => "at",
|
||||
"params" => [
|
||||
"qq" => "456",
|
||||
],
|
||||
"start" => 17,
|
||||
"end" => 30,
|
||||
],
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
## CQ 码列表
|
||||
|
||||
### CQ::face() - 发送 QQ 表情
|
||||
@@ -119,7 +179,7 @@ $str = CQ::removeCQ("[CQ:at,qq=all]这是带表情的全体消息[CQ:face,id=8]"
|
||||
|
||||
定义:`CQ::face($id)`
|
||||
|
||||
参数:`$id` 为 QQ 表情对应的 ID 号,一些常见的表情 ID 对应的表情样式见 [炸毛框架 1.x 版本文档](https://docs-v1.zhamao.xin/face_list.html)。
|
||||
参数:`$id` 为 QQ 表情对应的 ID 号,一些常见的表情 ID 对应的表情样式见 [QQ 对应表情ID表](https://static.zhamao.me/face_id.html)。
|
||||
|
||||
```php
|
||||
/**
|
||||
@@ -449,11 +509,31 @@ public function xmlTest() {
|
||||
|
||||
发送 QQ 兼容的 JSON 多媒体消息。
|
||||
|
||||
定义:`CQ::json($data)`
|
||||
定义:`CQ::json($data, $resid = 0)`
|
||||
|
||||
参数同上,内含 JSON 字符串即可。
|
||||
|
||||
其中 `$resid` 是面向 go-cqhttp 扩展的参数,默认不填为 0,走小程序通道,填了走富文本通道发送。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
因为某些众所周知的原因,XML 和 JSON 的返回不提供实例,有兴趣的可以自行研究如何编写,文档不含任何相关教程。
|
||||
|
||||
### CQ::_custom() - 扩展自定义 CQ 码
|
||||
|
||||
用于兼容各类含有被支持的扩展 CQ 码,比如 go-cqhttp 的 `[CQ:gift]` 礼物类型。
|
||||
|
||||
定义:`CQ::_custom(string $type_name, array $params)`
|
||||
|
||||
| 参数名 | 说明 |
|
||||
| ----------- | --------------------------------------------------- |
|
||||
| `type_name` | CQ 码类型,如 `music`,`at` |
|
||||
| `params` | 发送的 CQ 码中的参数数组,例如 `["qq" => "123456"]` |
|
||||
|
||||
下面是一个例子:
|
||||
|
||||
```php
|
||||
CQ::_custom("at",["qq" => "123456","qwe" => "asd"]);
|
||||
// 返回:[CQ:at,qq=123456,qwe=asd]
|
||||
```
|
||||
|
||||
179
docs/component/bot/message-util.md
Normal file
179
docs/component/bot/message-util.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# MessageUtil 消息处理工具类
|
||||
|
||||
类定义:`\ZM\Utils\MessageUtil`
|
||||
|
||||
> 2.3.0 版本起可用。
|
||||
|
||||
这里放置一些机器人聊天消息处理的便捷静态方法,例如下载图片等。
|
||||
|
||||
## 方法
|
||||
|
||||
### downloadCQImage()
|
||||
|
||||
下载用户消息中所带的所有图片,并返回文件路径。
|
||||
|
||||
定义:`downloadCQImage($msg, $path = null)`
|
||||
|
||||
参数 `$msg` 为带图片的用户消息,例如 `你好啊!\n[CQ:image,file=a.jpg,url=https://zhamao.xin/file/hello.jpg]`
|
||||
|
||||
参数 `$path` 为图片下载的路径,如果不填(默认 null)则指定为 `zm_data/images/` 目录,且不存在会自动创建。
|
||||
|
||||
```php
|
||||
$r = MessageUtil::downloadCQImage("你好啊!\n[CQ:image,file=a.jpg,url=https://zhamao.xin/file/hello.jpg]");
|
||||
/*
|
||||
$r == [
|
||||
"/path-to/zhamao-framework/zm_data/images/a.jpg"
|
||||
];
|
||||
*/
|
||||
```
|
||||
|
||||
如果返回的是空数组 `[ ]`,则表明消息中没有图片。如果返回的是 `false`,则表明其中至少一张下载失败或路径有误。
|
||||
|
||||
### containsImage()
|
||||
|
||||
检查消息中是否含有图片。
|
||||
|
||||
定义:`containsImage($msg)`
|
||||
|
||||
返回:`bool`,你懂的,true 就是有,false 就没有。
|
||||
|
||||
```php
|
||||
MessageUtil::containsImage("[CQ:image,file=a.jpg,url=http://xxx]"); // true
|
||||
MessageUtil::containsImage("[CQ:face,id=140] 咦,这是一条带表情的消息"); // false
|
||||
```
|
||||
|
||||
### isAtMe()
|
||||
|
||||
检查消息中是否含有@bot的消息。
|
||||
|
||||
定义:`isAtMe($msg, $me_id)`
|
||||
|
||||
参数 `$me_id` 为Bot的QQ号。
|
||||
|
||||
返回:`bool`,true 就是有,false 就没有。
|
||||
|
||||
```php
|
||||
MessageUtil::isAtMe("[CQ:at,qq=123456]炸毛你好","123456"); // true
|
||||
MessageUtil::isAtMe("[CQ:at,qq=123456789]另一个朋友你好","123456"); // false
|
||||
```
|
||||
|
||||
### getImageCQFromLocal()
|
||||
|
||||
通过文件路径获取图片的发送 CQ 码。
|
||||
|
||||
定义:`getImageCQFromLocal($file, $type = 0)`
|
||||
|
||||
参数 `$file` 为图片的绝对路径。
|
||||
|
||||
返回:图片的 CQ 码,如 `[CQ:image,file=xxxxx]`
|
||||
|
||||
参数 `$type`:
|
||||
|
||||
- `0`:以 base64 的方式发送图片,返回结果如 `[CQ:image,file=base64://xxxxxx]`
|
||||
- `1`:以 `file://` 本地文件的方式发送图片,返回结果如 `[CQ:image,file=file:///path-to/images/a.jpg]`
|
||||
- `2`:返回图片的 http:// CQ 码(默认为 /images/ 路径就是文件对应所在的目录),如 `[CQ:image,file=http://127.0.0.1:20001/images/a.jpg]`
|
||||
|
||||
### splitCommand()
|
||||
|
||||
切割用户消息为数组形式(`@CQCommand` 就是使用此方式切割的)
|
||||
|
||||
定义:`splitCommand($msg): array`
|
||||
|
||||
返回:数组,切分后的。
|
||||
|
||||
!!! tip "为什么不直接使用 explode 呢"
|
||||
|
||||
因为 `explode()` 只会简单粗暴的切割字符串,假设用户输入的消息中两个词中间有多个空格,则会有空的词出现。例如 `你好 我是一个长空格`。此函数会将多个空格当作一个空格来对待。
|
||||
|
||||
```php
|
||||
MessageUtil::splitCommand("你好 我是傻瓜\n我是傻瓜二号"); // ["你好","我是傻瓜","我是傻瓜二号"]
|
||||
MessageUtil::splitCommand("我有 三个空格"); // ["我有","三个空格"]
|
||||
```
|
||||
|
||||
### matchCommand()
|
||||
|
||||
匹配一条消息到 `@CQCommand` 规则的注解事件,返回要执行的类和函数位置。
|
||||
|
||||
定义:`matchCommand($msg, $obj)`
|
||||
|
||||
参数 `$msg` 为消息内容。
|
||||
|
||||
参数 `$obj` 为事件的对象,可使用 `ctx()->getData()` 获取原先的事件体(仅限 OneBot 消息类型事件中使用)
|
||||
|
||||
返回:`\ZM\Entity\MatchObject` 对象,含有匹配成功与否,匹配到的注解对象,匹配到的分割词等,见 []
|
||||
|
||||
### addShortCommand()
|
||||
|
||||
快速添加一条静态消息回复命令。
|
||||
|
||||
定义:`addShortCommand($command, string $reply)`
|
||||
|
||||
参数 `$command` 为问的内容,如 `炸毛不聪明`。
|
||||
|
||||
参数 `$reply` 为回复的内容,如 `其实还是很聪明的!`。
|
||||
|
||||
这个命令推荐在 `@OnStart` 注解下使用,可以用这个来做一个动态的词库,从文件加载后使用。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OnStart()
|
||||
*/
|
||||
public function onStart() {
|
||||
MessageUtil::addShortCommand("炸毛不聪明", "其实还是很聪明的!");
|
||||
}
|
||||
```
|
||||
|
||||
<chat-box>
|
||||
) 炸毛不聪明
|
||||
( 其实还是很聪明的!
|
||||
</chat-box>
|
||||
|
||||
### strToArray()
|
||||
|
||||
将 `string` 类型的消息文本转换为 `array` 格式。
|
||||
|
||||
定义:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)`
|
||||
|
||||
参数 `$msg` 为带 OB/CQ 码的字符串消息,如 `你好啊,[CQ:at,qq=123]`。
|
||||
|
||||
参数 `$ignore_space` 在 `false` 时,转换的数组内会包含空 `text` 段。
|
||||
|
||||
参数 `$trim_text` 为 `true` 时,会自动去除 `text` 段消息头尾的换行符和空格。
|
||||
|
||||
这个命令转换的数组格式符合 OneBot 11/12 标准,但细节上可能会与不同 OneBot 实现有所差异。
|
||||
|
||||
```php
|
||||
$str = "你好啊,[CQ:at,qq=123]";
|
||||
$arr = \ZM\Utils\MessageUtil::strToArray($str);
|
||||
```
|
||||
|
||||
转换结果参考如下:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "text",
|
||||
"data": {
|
||||
"text": "你好啊,"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "at",
|
||||
"data": {
|
||||
"qq": "123"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### arrayToStr()
|
||||
|
||||
将 `array` 格式的消息内容转换为字符串 + CQ 码的形式。
|
||||
|
||||
定义:`arrayToStr(array $array)`
|
||||
|
||||
```php
|
||||
// 我们使用上边的 $arr 作为传入值。
|
||||
$new_str = \ZM\Utils\MessageUtil::arrayToStr($arr);
|
||||
// 结果:"你好啊,[CQ:at,qq=123]"
|
||||
```
|
||||
9
docs/component/bot/robot-api-12.md
Normal file
9
docs/component/bot/robot-api-12.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 机器人 API(OneBotV12)(待发布)
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
目前由于 OneBot 12 标准还没有定稿,处于草案阶段,故框架暂不更新。
|
||||
|
||||
在未来升级到 OneBot 12 标准后,框架会提供转换及兼容措施以及 12 版本的 API 方法。
|
||||
|
||||
见 [机器人动作(OneBot 11)](../robot-api)。
|
||||
@@ -1,26 +1,29 @@
|
||||
# 机器人 API(ZMRobot)
|
||||
# 机器人 API(OneBotV11)
|
||||
|
||||
ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。
|
||||
OneBotV11 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。
|
||||
|
||||
| 属性项 | 属性值 | 备注 |
|
||||
| -------- | ---------------- | ------------------------------ |
|
||||
| 名称 | ZMRobot | |
|
||||
| 类型 | 实例化类 | `$r = new ZMRobot($conn)` |
|
||||
| 命名空间 | `ZM\API\ZMRobot` | 使用前先 `use ZM\API\ZMRobot;` |
|
||||
| 属性项 | 属性值 | 备注 |
|
||||
| -------- | ------------------ | -------------------------------- |
|
||||
| 名称 | OneBotV11 | |
|
||||
| 类型 | 实例化类 | `$r = new OneBotV11($conn)` |
|
||||
| 命名空间 | `ZM\API\OneBotV11` | 使用前先 `use ZM\API\OneBotV11;` |
|
||||
| 别名 | `ZM\API\ZMRobot` | 此类目前是 `extends OneBotV11` |
|
||||
|
||||
> 你也可以继续使用 2.5 版本之前的别名类 `ZMRobot`,但未来框架将会优先兼容 OneBot V12 版本的协议,可能会造成更新问题,建议切换为 OneBotV11 类。
|
||||
|
||||
## 属性
|
||||
|
||||
对象属性方法是对 API 的调整,例如是否以 `_async`、`_rate_limited` 后缀发送 API、设置协程返回还是异步返回结果等。
|
||||
|
||||
### ZMRobot::API_NORMAL
|
||||
### OneBotV11::API_NORMAL
|
||||
|
||||
以默认(无后缀)方式请求 API。
|
||||
|
||||
### ZMRobot::API_ASYNC
|
||||
### OneBotV11::API_ASYNC
|
||||
|
||||
以后缀 `_async` 方式异步请求 API。
|
||||
|
||||
### ZMRobot::API_RATE_LIMITED
|
||||
### OneBotV11::API_RATE_LIMITED
|
||||
|
||||
以后缀 `_rate_limited` 方式请求 API。
|
||||
|
||||
@@ -30,7 +33,7 @@ ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机
|
||||
|
||||
设置后缀。目前支持 `_async`、`_rate_limited`。
|
||||
|
||||
- **prefix**: `int` `默认:API_NORMAL`,可选 `ZMRobot::API_NORMAL`、`ZMRobot::API_ASYNC`、`ZMRobot::API_RATE_LIMITED`
|
||||
- **prefix**: `int` `默认:API_NORMAL`,可选 `OneBotV11::API_NORMAL`、`OneBotV11::API_ASYNC`、`OneBotV11::API_RATE_LIMITED`
|
||||
|
||||
设置后缀后,请求的 API 会发生变化。例如发送私聊消息:`sendPrivateMsg()`,请求的 API 为 `send_private_msg_async`,详见 [OneBot 文档](https://github.com/howmanybots/onebot/blob/master/v11/specs/api/README.md)。
|
||||
|
||||
@@ -43,22 +46,22 @@ ZMRobot 类是封装好的 OneBot 标准的 API 接口调用类,可以在机
|
||||
获取当前对象的机器人 QQ 或 OneBot 实例的 ID。
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get(123456);
|
||||
$bot = OneBotV11::get(123456);
|
||||
echo $bot->getSelfId(); //123456
|
||||
```
|
||||
|
||||
### ZMRobot::get()
|
||||
### OneBotV11::get()
|
||||
|
||||
静态方法,用来通过机器人 QQ 或 OneBot 实例的 ID 获取 ZMRobot 对象。
|
||||
静态方法,用来通过机器人 QQ 或 OneBot 实例的 ID 获取 OneBotV11对象。
|
||||
|
||||
参数:`$robot_id`,必填。
|
||||
|
||||
```php
|
||||
$r = ZMRobot::get(123456);
|
||||
$r = OneBotV11::get(123456);
|
||||
$r->sendPrivateMsg(55555, "hello");
|
||||
```
|
||||
|
||||
### ZMRobot::getRandom()
|
||||
### OneBotV11::getRandom()
|
||||
|
||||
静态方法,随机获取一个连接到框架的机器人(多个机器人实例连接到框架时适用)。
|
||||
|
||||
@@ -66,21 +69,21 @@ $r->sendPrivateMsg(55555, "hello");
|
||||
|
||||
```php
|
||||
try {
|
||||
$bot = ZMRobot::getRandom();
|
||||
$bot = OneBotV11::getRandom();
|
||||
$bot->sendPrivateMsg(55555, "foo");
|
||||
} catch (\ZM\Exception\RobotNotFoundException $e) {
|
||||
echo "还没有机器人连接到框架!\n";
|
||||
}
|
||||
```
|
||||
|
||||
### ZMRobot::getAllRobot()
|
||||
### OneBotV11::getAllRobot()
|
||||
|
||||
获取所有连接到框架的机器人的 ZMRobot 对象。
|
||||
获取所有连接到框架的机器人的 OneBotV11 对象。
|
||||
|
||||
返回值:`ZMRobot[]`。
|
||||
返回值:`OneBotV11[]`。
|
||||
|
||||
```php
|
||||
$all = ZMRobot::getAllRobot();
|
||||
$all = OneBotV11::getAllRobot();
|
||||
foreach($all as $v) {
|
||||
$v->sendPrivateMsg(55555, "机器人轮流给一个人发消息啦!");
|
||||
}
|
||||
@@ -95,7 +98,7 @@ foreach($all as $v) {
|
||||
```php
|
||||
//从上下文获取 Websocket 连接对象
|
||||
$conn = ctx()->getConnection();
|
||||
$bot = new ZMRobot($conn);
|
||||
$bot = new OneBotV11($conn);
|
||||
```
|
||||
|
||||
## 返回结果处理
|
||||
@@ -103,7 +106,7 @@ $bot = new ZMRobot($conn);
|
||||
因为框架的机器人是兼容 OneBot 标准的(原 CQHTTP),所以每次接收发送 API 请求的结果都是大体一样的结构。我们以 `sendPrivateMsg()` 为例,因为发送出去的每一条消息都会在 OneBot 实例(如 CQHTTP 插件、go-cqhttp 等)中对应一个消息 ID,以供我们核查消息和后续撤回等操作需要。
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get("123456"); // 机器人QQ号
|
||||
$bot = OneBotV11::get("123456"); // 机器人QQ号
|
||||
$obj = $bot->sendGroupMsg("234567", "你好");
|
||||
echo json_encode($obj, 128|256);
|
||||
```
|
||||
@@ -163,7 +166,7 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
|
||||
=== "代码"
|
||||
|
||||
```php
|
||||
$bot = ZMRobot::get(123456); // 123456是你的机器人QQ
|
||||
$bot = OneBotV11::get(123456); // 123456是你的机器人QQ
|
||||
$bot->sendPrivateMsg("627577391", "你好啊!你好你好!");
|
||||
```
|
||||
|
||||
@@ -766,6 +769,21 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
|
||||
|
||||
响应数据:无
|
||||
|
||||
### getExtendedAPI()
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。与下方 `callExtendedAPI` 不同的是,为了方便用户使用,炸毛框架内置了热门使用并且相对稳定的机器人客户端的专有 API。
|
||||
|
||||
目前内置了 `go-cqhttp` 频道相关的扩充 API。
|
||||
|
||||
使用示例:`getExtendedAPI('go-cqhttp')->getGuildList()`
|
||||
使用示例2:`getExtendedAPI()->sendGuildChannelMsg($guild_id, $channel_id, '频道的消息')`
|
||||
|
||||
唯一一个参数做保留,用于选择不同客户端,目前仅支持 `go-cqhttp`,所以缺省也默认为 `go-cqhttp`。
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。
|
||||
|
||||
### callExtendedAPI() (扩充 API)
|
||||
|
||||
用来调用 OneBot 标准之外扩展出来的自定义 API。
|
||||
86
docs/component/bot/turing-api.md
Normal file
86
docs/component/bot/turing-api.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# 图灵机器人 API(TuringAPI)
|
||||
|
||||
类定义:`\ZM\API\TuringAPI`
|
||||
|
||||
## 方法
|
||||
|
||||
### TuringAPI::getTuringMsg()
|
||||
|
||||
请求图灵接口,返回回复的消息。
|
||||
|
||||
定义:`getTuringMsg($msg, $user_id, $api)`
|
||||
|
||||
参数 `$msg` 为用户的消息内容,如果含有图片 CQ 码,则自动转换为图灵兼容的接口模式。
|
||||
|
||||
参数 `$user_id` 为用户 ID,一般默认给 QQ 号码就可以了,注意最好不要有特殊字符(如 `./\<>*` 等),否则会间断性调用失败。
|
||||
|
||||
参数 `$api` 为图灵机器人的 `apikey`,可以到 <http://www.turingapi.com/> 申请免费或付费的 API key。
|
||||
|
||||
在框架的示例模块中,已经写好了一个正常机器人响应图灵回复的命令,如下:
|
||||
|
||||
```php
|
||||
class Hello {
|
||||
/**
|
||||
* 图灵机器人的内置实现,在www.turingapi.com申请一个apikey填入下方变量即可。
|
||||
* @CQCommand(start_with="机器人",end_with="机器人",message_type="group")
|
||||
* @CQMessage(message_type="private",level=1)
|
||||
*/
|
||||
public function turingAPI() {
|
||||
$user_id = ctx()->getUserId();
|
||||
$api = ""; // 请在这里填入你的图灵机器人的apikey
|
||||
if ($api === "") return false; //如果没有填入apikey则此功能关闭
|
||||
if (($this->_running_annotation ?? null) instanceof CQCommand) {
|
||||
$msg = ctx()->getFullArg("我在!有什么事吗?");
|
||||
} else {
|
||||
$msg = ctx()->getMessage();
|
||||
}
|
||||
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);
|
||||
Console::info(ctx()->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如上述代码,我们将申请的 apikey 填入变量 `$api` 中,启动机器人即可使用,以下是实测消息(我用自己申请的 key 做测试回复的消息)。
|
||||
|
||||
<chat-box>
|
||||
) 你咋了
|
||||
( 我没事哦,谢谢您的关心。
|
||||
) 上海天气
|
||||
( 上海:周一 03月29日,小雨 东南风转东风,最低气温14度,最高气温24度。
|
||||
^ 切换为群内
|
||||
) 机器人
|
||||
( 我在!有什么事吗?
|
||||
) 你叫啥
|
||||
( 我的名字叫炸毛,认识你很高兴呢!
|
||||
</chat-box>
|
||||
|
||||
在默认示例模块中的例子是直接可以拿来用的,这段代码同时做了对 at 的处理、以及兼容用户自定义写的其他命令的方式,下面是默认模块填好 apikey 后可以用的各种方式提问:
|
||||
|
||||
<chat-box>
|
||||
^ 切换为群内
|
||||
) 我是一条普通消息,这条机器人不会回复我
|
||||
) @机器人 你叫啥
|
||||
( 我是聪明可爱的炸毛,认识你很高兴。
|
||||
) 机器人
|
||||
( 我在!有什么事吗?
|
||||
) 一言
|
||||
( 多少事,从来急,天地转,光阴迫,一万年太久,只争朝夕。
|
||||
</chat-box>
|
||||
@@ -25,7 +25,7 @@ vendor/bin/start server --log-error # 以 error 等级启动框架
|
||||
vendor/bin/start server --log-warning # 以 warning 等级启动框架
|
||||
vendor/bin/start server --log-info # 以 info 等级启动框架
|
||||
vendor/bin/start server --log-verbose # 以 verbose 等级启动框架
|
||||
vendor/bin/start server --log-debug # 以 debug 等级 启动框架
|
||||
vendor/bin/start server --log-debug # 以 debug 等级启动框架
|
||||
```
|
||||
|
||||
## 使用 Log 输出内容
|
||||
@@ -100,11 +100,9 @@ $str = Console::setColor("I am gold color.", "gold");
|
||||
|
||||
炸毛框架支持从终端输入命令来进行一些操作,例如重启框架、停止框架、执行函数等。
|
||||
|
||||
::: warning 注意
|
||||
!!! warning 注意
|
||||
|
||||
在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。
|
||||
|
||||
:::
|
||||
在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。
|
||||
|
||||
### reload
|
||||
|
||||
@@ -160,6 +158,8 @@ color green 我是绿色的字
|
||||
|
||||
文件位置:`config/motd.txt`
|
||||
|
||||
其中,默认的 `Zhamao` 字样的 MOTD 是使用 **figlet** 命令生成的,`figlet "Zhamao"`,你也可以针对自己的机器人名称或品牌进行生成。
|
||||
|
||||
## 设置输出主题
|
||||
|
||||
Console 组件支持为多种不同的终端设置不同的主题,比如有些人喜欢使用白色的终端,但是白色终端下 info 的颜色很浅,看不到,还有人使用不能显示颜色的黑白终端.....
|
||||
@@ -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());
|
||||
}
|
||||
```
|
||||
|
||||
@@ -124,7 +124,7 @@ public function ping() {
|
||||
|
||||
## getRobot() - 获取机器人 API 对象
|
||||
|
||||
返回当前上下文关联的机器人 API 调用对象 [ZMRobot](robot-api.md)。
|
||||
返回当前上下文关联的机器人 API 调用对象 [ZMRobot](../bot/robot-api.md)。
|
||||
|
||||
可以使用的事件:所有 HTTP API 发来的事件:`@CQCommand()`,`@CQMessage()` 等。
|
||||
|
||||
@@ -421,4 +421,5 @@ public function argTest1() {
|
||||
<chat-box>
|
||||
) test abc 334 argtest
|
||||
( 参数内容:abc, 334, argtest
|
||||
</chat-box>
|
||||
</chat-box>
|
||||
|
||||
104
docs/component/common/event-tracer.md
Normal file
104
docs/component/common/event-tracer.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 事件跟踪器及调试
|
||||
|
||||
众所周知,炸毛框架中的事件由内置的事件分发器(EventDispatcher)负责分发,但调试事件分发在之前的版本比较困难,例如不能获取到事件如何被调用,以及事件如何被捕获。
|
||||
|
||||
EventTracer 的作用是记录事件的调用顺序,以便于调试。
|
||||
|
||||
命名空间使用指南:`use ZM\Event\EventTracer;`
|
||||
|
||||
## EventTracer::getCurrentEvent() - 获取当前注解事件对象
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OnStart()
|
||||
*/
|
||||
public function onStart() {
|
||||
zm_dump(EventTracer::getCurrentEvent());
|
||||
}
|
||||
/*
|
||||
^ ZM\Annotation\Swoole\OnStart^ {#192
|
||||
+worker_id: 0
|
||||
+method: "onStart"
|
||||
+class: "Module\Example\Hello"
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
这里这个方法必须在注解事件内执行,如果在注解事件外执行,将会返回 `null`。
|
||||
|
||||
## EventTracer::getCurrentEventMiddlewares() - 获取当前注解事件的中间件们
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OnStart()
|
||||
* @Middleware("timer")
|
||||
*/
|
||||
public function onStart() {
|
||||
zm_dump(EventTracer::getCurrentEventMiddlewares());
|
||||
}
|
||||
/*
|
||||
^ array:1 [
|
||||
0 => ZM\Annotation\Http\Middleware^ {#194
|
||||
+middleware: "timer"
|
||||
+params: []
|
||||
+method: "onStart"
|
||||
+class: "Module\Example\Hello"
|
||||
}
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
返回值为当前注解事件的中间件们,如果没有注解中间件,返回 `[]`。
|
||||
|
||||
## EventTracer::getEventTraceList() - 获取注解事件的列表
|
||||
|
||||
此处返回的是 `getCurrentEvent()` 相同的对象,但是返回的是一个数组,数组中的元素是注解事件。
|
||||
|
||||
```php
|
||||
/**
|
||||
* 一个简单随机数的功能demo
|
||||
* 问法1:随机数 1 20
|
||||
* 问法2:从1到20的随机数
|
||||
* @CQCommand("随机数")
|
||||
* @Middleware("timer")
|
||||
* @CQCommand(pattern="*从*到*的随机数")
|
||||
* @return string
|
||||
*/
|
||||
public function randNum() {
|
||||
// 此处为随机数代码
|
||||
zm_dump(EventTracer::getEventTraceList());
|
||||
return "随机数:" . rand(1, 20);
|
||||
}
|
||||
|
||||
/*
|
||||
^ array:2 [
|
||||
0 => ZM\Annotation\CQ\CQCommand^ {#193
|
||||
+match: ""
|
||||
+pattern: "*从*到*的随机数"
|
||||
+regex: ""
|
||||
+start_with: ""
|
||||
+end_with: ""
|
||||
+keyword: ""
|
||||
+alias: []
|
||||
+message_type: ""
|
||||
+user_id: 0
|
||||
+group_id: 0
|
||||
+discuss_id: 0
|
||||
+level: 20
|
||||
+method: "randNum"
|
||||
+class: "Module\Example\Hello"
|
||||
}
|
||||
1 => ZM\Annotation\Swoole\OnMessageEvent^ {#165
|
||||
+connect_type: "default"
|
||||
+rule: "connectIsQQ()"
|
||||
+level: 99
|
||||
+method: "handleByEvent"
|
||||
+class: "ZM\Module\QQBot"
|
||||
}
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
## EventDispatcher::enableEventTrace() - 启用事件跟踪器
|
||||
|
||||
还没写完,不着急。
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
## getClassPath()
|
||||
|
||||
[源码](https://github.com/zhamao-robot/zhamao-framework/blob/master/src/ZM/global_functions.php#L24)
|
||||
|
||||
根据加载的用户编写的代码类名来获取类所在的文件路径。
|
||||
|
||||
=== "src/Module/Example/Hello.php"
|
||||
@@ -38,6 +40,8 @@
|
||||
|
||||
## explodeMsg()
|
||||
|
||||
[源码](https://github.com/zhamao-robot/zhamao-framework/blob/master/src/ZM/global_functions.php#L39)
|
||||
|
||||
切割字符串的函数,支持多空格,换行,tab。
|
||||
|
||||
定义:`explodeMsg($msg, $ban_comma = false)`
|
||||
@@ -49,6 +53,8 @@ echo json_encode($s, 128|256); // ["你好啊","你好你好","我还有多个
|
||||
|
||||
## unicode_decode()
|
||||
|
||||
[源码](https://github.com/zhamao-robot/zhamao-framework/blob/master/src/ZM/global_functions.php#L54)
|
||||
|
||||
Unicode 解码,一般用于被转义的 Unicode 转回来。
|
||||
|
||||
```php
|
||||
@@ -57,6 +63,8 @@ echo unicode_decode("\u4f60\u597d"); // 你好
|
||||
|
||||
## matchPattern()
|
||||
|
||||
[源码](https://github.com/zhamao-robot/zhamao-framework/blob/master/src/ZM/global_functions.php#L91)
|
||||
|
||||
根据星号匹配字符串(非正则表达式)。
|
||||
|
||||
匹配示例:
|
||||
@@ -81,6 +89,8 @@ matchPattern("*把*翻译成*", "请把你好翻译成阿拉伯语"); // true
|
||||
|
||||
## split_explode()
|
||||
|
||||
[源码](https://github.com/zhamao-robot/zhamao-framework/blob/master/src/ZM/global_functions.php#L103)
|
||||
|
||||
和 `explodeMsg()` 类似,用作分割字符串,不过此函数加入了对 `中文|数字` 两者的分割,也就是说中文和数字之间也会被分割。
|
||||
|
||||
定义:`split_explode($del, $str, $divide_en = false)`
|
||||
@@ -97,6 +107,8 @@ split_explode(" ", "前进20 急啊急啊"); // ["前进","20","急啊急啊"]
|
||||
|
||||
## matchArgs()
|
||||
|
||||
[源码](https://github.com/zhamao-robot/zhamao-framework/blob/master/src/ZM/global_functions.php#L135)
|
||||
|
||||
`matchPattern()` 的扩展,如果 `matchPattern()` 格式的字符串和模式匹配成功,则通过星号位置来提取星号匹配到的内容,参数同 `matchPattern()`。
|
||||
|
||||
```php
|
||||
@@ -134,10 +146,6 @@ set_coroutine_params(["data" => [
|
||||
|
||||
别名:`context()`,获取当前协程的上下文,见 [上下文](/component/context/)。
|
||||
|
||||
## zm_debug()
|
||||
|
||||
同 `Console::debug($msg)`。
|
||||
|
||||
## zm_sleep()
|
||||
|
||||
协程版 `sleep()` 函数。
|
||||
@@ -227,5 +235,90 @@ bot()->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 连接号。
|
||||
|
||||
## zm_dump()
|
||||
|
||||
更漂亮地输出变量值,可替代 `var_dump()`。
|
||||
|
||||
```php
|
||||
class Pass {
|
||||
public $foo = 123;
|
||||
public $bar = ["a", "b"];
|
||||
}
|
||||
$pass = new Pass();
|
||||
$pass->obj = true;
|
||||
zm_dump($pass);
|
||||
```
|
||||
|
||||

|
||||
|
||||
## zm_config()
|
||||
|
||||
> v2.4.0 起可用。
|
||||
|
||||
同 `ZMConfig::get()`。
|
||||
|
||||
定义:`zm_config($name, $key = null)`。
|
||||
|
||||
有关 ZMConfig 模块的说明,见 [指南 - 基本配置](/guide/basic-config/)。
|
||||
|
||||
```php
|
||||
zm_config("global"); //等同于 ZMConfig::get("global");
|
||||
zm_config("global", "swoole"); //等同于 ZMConfig::get("global", "swoole");
|
||||
```
|
||||
|
||||
## zm_info()
|
||||
|
||||
> v2.4.0 起可用。(下面的 log 类也一样)
|
||||
|
||||
同 `Console::info($msg)`。
|
||||
|
||||
## zm_debug()
|
||||
|
||||
同 `Console::debug($msg)`。
|
||||
|
||||
## zm_warning()
|
||||
|
||||
同 `Console::warning($msg)`。
|
||||
|
||||
## zm_success()
|
||||
|
||||
同 `Console::success($msg)`。
|
||||
|
||||
## zm_error()
|
||||
|
||||
同 `Console::error($msg)`。
|
||||
|
||||
## zm_verbose()
|
||||
|
||||
同 `Console::verbose($msg)`。
|
||||
|
||||
37
docs/component/common/remote-terminal.md
Normal file
37
docs/component/common/remote-terminal.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 远程终端
|
||||
框架在 2.3 版本时删除了本地终端(就是框架启动后可以在终端输入一些参数),因为框架的多进程模式会导致终端输入错乱,所以暂时取消掉了。
|
||||
|
||||
而远程终端应运而生,为的是弥补这一功能。与之前不同的是,远程终端使用 nc 连接,无需任何其他组件和客户端,而且功能更丰富,支持自定义命令。
|
||||
|
||||
## 启用
|
||||
有两种开启方式:
|
||||
|
||||
- 永久开启:全局配置文件中找到 `remote_terminal` 的 `status`,改为 true,启动框架即可。
|
||||
- 临时开启:启动框架时加上参数 `--remote-terminal`。例如:`vendor/bin/start server --remote-terminal`。
|
||||
|
||||
## 配置
|
||||
在一般情况下,框架为了安全,直接按照默认配置,会监听 `127.0.0.1:20002` 端口,不可以远程访问,只能使用本机的 nc 连接,效果如下:
|
||||
|
||||
本地主机:
|
||||

|
||||
|
||||
从别的主机:
|
||||

|
||||
|
||||
如果将 `host` 改为 `0.0.0.0` 或对应监听地址,即可指向性访问。
|
||||
|
||||
但是,如果你又想远程连接,又想保证安全,那么可以设置一个 token 参数,来保证连接时需要输入 token 才能使用远程终端。
|
||||
假设我们的 token 是 `iAMTokEn`:
|
||||

|
||||
|
||||
## 使用
|
||||
默认情况下,使用 `nc` 命令即可。
|
||||
```bash
|
||||
nc <your-host> <your-port> -vvv
|
||||
# nc 127.0.0.1 20002 -vvv
|
||||
```
|
||||
|
||||
输入 help 即可查看内置的常用指令:
|
||||

|
||||
26
docs/component/common/task-worker.md
Normal file
26
docs/component/common/task-worker.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# TaskManager 工作进程管理
|
||||
|
||||
此类管理的是 TaskWorker 相关工作。有关使用 TaskWorker 的教程,见 [进阶 - 使用 TaskWorker 进程处理密集运算](/advanced/task-worker)
|
||||
|
||||
类定义:`\ZM\Utils\Manager\TaskManager`
|
||||
|
||||
使用 TaskWorker 需要先在 `global.php` 配置文件中开启!
|
||||
|
||||
## 方法
|
||||
|
||||
### runTask()
|
||||
|
||||
在 TaskWorker 运行任务。
|
||||
|
||||
定义:`runTask($task_name, $timeout = -1, ...$params)`
|
||||
|
||||
参数 `$task_name`:对应 `@OnTask` 注解绑定的任务函数。
|
||||
|
||||
参数 `$timeout`:等待任务函数最长运行的时间(秒),如果超过此时间将返回 false。
|
||||
|
||||
参数 `剩余`:将变量传入 TaskWorker 进程,除 Closure,资源类型外,可序列化的变量均可。
|
||||
|
||||
```php
|
||||
TaskManager::runTask("heavy_task", 100, "param1", "param2");
|
||||
```
|
||||
|
||||
67
docs/component/common/zmutil.md
Normal file
67
docs/component/common/zmutil.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# ZMUtil 杂项工具类
|
||||
|
||||
调用前先 use:`use ZM\Utils\ZMUtil;`
|
||||
|
||||
## ZMUtil::stop()
|
||||
|
||||
停止框架运行。
|
||||
|
||||
## ZMUtil::reload()
|
||||
|
||||
重载框架,这会断开所有到框架的连接和重载所有 `src/` 目录下的用户源码并重新加载所有 Worker 进程。
|
||||
|
||||
## ZMUtil::getModInstance()
|
||||
|
||||
根据类名称拿到此类的单例(前提是目标的类的构造函数为空)。
|
||||
|
||||
```php
|
||||
class ASD{
|
||||
public $test = 0;
|
||||
}
|
||||
ZMUtil::getModInstance(ASD::class)->test = 5;
|
||||
```
|
||||
|
||||
## ZMUtil::getReloadableFiles()
|
||||
|
||||
返回可通过热重启(reload)来重新加载的 php 文件列表。
|
||||
|
||||
以下是示例模块下的例子(直接拉取最新的框架源码并运行框架后获取的)。
|
||||
|
||||
```php
|
||||
array:31 [
|
||||
94 => "src/ZM/Context/Context.php"
|
||||
95 => "src/ZM/Context/ContextInterface.php"
|
||||
96 => "src/ZM/Annotation/AnnotationParser.php"
|
||||
97 => "src/Custom/Annotation/Example.php"
|
||||
98 => "src/ZM/Annotation/Interfaces/CustomAnnotation.php"
|
||||
99 => "src/Module/Example/Hello.php"
|
||||
100 => "src/ZM/Annotation/Swoole/OnStart.php"
|
||||
101 => "src/ZM/Annotation/CQ/CQCommand.php"
|
||||
102 => "src/ZM/Annotation/Interfaces/Level.php"
|
||||
103 => "src/ZM/Annotation/Command/TerminalCommand.php"
|
||||
104 => "src/ZM/Annotation/Http/RequestMapping.php"
|
||||
105 => "src/ZM/Annotation/Http/RequestMethod.php"
|
||||
106 => "src/ZM/Annotation/Http/Middleware.php"
|
||||
107 => "src/ZM/Annotation/Interfaces/ErgodicAnnotation.php"
|
||||
108 => "src/ZM/Annotation/Swoole/OnOpenEvent.php"
|
||||
109 => "src/ZM/Annotation/Swoole/OnSwooleEventBase.php"
|
||||
110 => "src/ZM/Annotation/Interfaces/Rule.php"
|
||||
111 => "src/ZM/Annotation/Swoole/OnCloseEvent.php"
|
||||
112 => "src/ZM/Annotation/Swoole/OnRequestEvent.php"
|
||||
113 => "src/ZM/Http/RouteManager.php"
|
||||
114 => "vendor/symfony/routing/RouteCollection.php"
|
||||
115 => "vendor/symfony/routing/Route.php"
|
||||
116 => "src/Module/Middleware/TimerMiddleware.php"
|
||||
117 => "src/ZM/Http/MiddlewareInterface.php"
|
||||
118 => "src/ZM/Annotation/Http/MiddlewareClass.php"
|
||||
119 => "src/ZM/Annotation/Http/HandleBefore.php"
|
||||
120 => "src/ZM/Annotation/Http/HandleAfter.php"
|
||||
121 => "src/ZM/Annotation/Http/HandleException.php"
|
||||
122 => "src/ZM/Event/EventManager.php"
|
||||
123 => "src/ZM/Annotation/Swoole/OnSwooleEvent.php"
|
||||
124 => "src/ZM/Event/EventDispatcher.php"
|
||||
]
|
||||
```
|
||||
|
||||
> 为什么不能重载所有文件?因为框架是多进程模型,而重载相当于只重新启动了一次 Worker 进程,Manager 和 Master 进程未重启,所以被 Manager、Master 进程已经加载的 PHP 文件无法使用 reload 命令重新加载。详见 [进阶 - 进程间隔离](/advanced/multi-process/#_5)。
|
||||
|
||||
54
docs/component/http/route-manager.md
Normal file
54
docs/component/http/route-manager.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# HTTP 路由管理
|
||||
|
||||
HTTP 路由管理器用作管理炸毛框架内 `@RequestMapping` 和静态目录的路由操作的,可在运行过程中编写添加路由。
|
||||
|
||||
类定义:`\ZM\Http\RouteManager`
|
||||
|
||||
> 2.3.0 版本起可用。
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
因为炸毛框架的路由实现是不基于跨进程的共享内存的,所以每次使用这里面的工具函数都需要单独在所有 Worker 进程中执行一次,最好的办法就是在启动框架时执行(`@OnStart(-1)` 即可,代表此注解事件将在每个工作进程中都被执行一次)。
|
||||
|
||||
## 方法
|
||||
|
||||
### importRouteByAnnotation()
|
||||
|
||||
通过注解类导入路由。(注:此方法一般为框架内部使用)
|
||||
|
||||
定义:`importRouteByAnnotation(RequestMapping $vss, $method, $class, $methods_annotations)`
|
||||
|
||||
参数 `$vss`:RequestMapping 注解类,类中定义 `route` 和 `request_method` 即可。
|
||||
|
||||
参数 `$method, $class`:执行的目标注解事件函数位置,比如 `$class = \Module\Example\Hello::class`,`$method = 'hitokoto'`。
|
||||
|
||||
参数 `$methods_annotations`:需要绑定的 Controller 注解类数组,一般数组内建议只带有一个 Controller,如 `[$controller]`。
|
||||
|
||||
### addStaticFileRoute()
|
||||
|
||||
添加一个单目录(此目录下无子目录,只有文件)并绑定为一个路由。
|
||||
|
||||
定义:`addStaticFileRoute($route, $path)`
|
||||
|
||||
参数 `$route`:绑定的目标路由,如 `/images/`。
|
||||
|
||||
参数 `$path`:绑定的文件目录位置,如 `/root/zhamao-framework-starter/zm_data/images/`。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @OnStart(-1)
|
||||
*/
|
||||
public function onStart() {
|
||||
RouteManager::addStaticFileRoute("/images/", DataProvider::getDataFolder()."images/");
|
||||
}
|
||||
```
|
||||
|
||||
## 属性
|
||||
|
||||
### RouteManager::$routes
|
||||
|
||||
此为存放路由树的变量,请谨慎操作。
|
||||
|
||||
定义:`\Symfony\Component\Routing\RouteCollection | null`
|
||||
|
||||
炸毛框架使用了 Symfony 框架的 route 组件,有关详情请查阅 [文档](https://symfony.com/doc/current/routing.html)。
|
||||
243
docs/component/module/module-pack.md
Normal file
243
docs/component/module/module-pack.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# 模块打包
|
||||
|
||||
从 2.5 版本起,炸毛框架的模块源码支持了打包和分发,开发者可以通过将自己的功能编写打包,并通过互联网进行分发,供其他人使用。此外,还提供了模块包热加载(不解包直接运行)和模块包解包功能。
|
||||
|
||||
## 构建模块包配置文件
|
||||
|
||||
炸毛框架的模块区分是根据 `src` 目录下的文件夹定义的,模块包的配置文件命名必须为 `zm.json`,此外,假设我们编写了一个最简单的模块,以脚手架生成的 Example 模块为例,文件夹结构如下:
|
||||
|
||||
```
|
||||
src/
|
||||
└── Module/
|
||||
├── Example/
|
||||
│ ├── Hello.php
|
||||
│ └── zm.json
|
||||
└── Middleware/
|
||||
└── TimerMiddleware.php
|
||||
```
|
||||
|
||||
我们在 Example 目录下创建一个 `zm.json` 的文件,编写配置,即代表 `src/Module/Example/` 文件夹及里面的用户模块源码为一个模块包,也就可以被框架识别并打包处理。
|
||||
|
||||
编写的配置文件结构如下:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "my-first-module"
|
||||
}
|
||||
```
|
||||
|
||||
对!你没看错,只需要定义一个 `name` 字段,即可声明这是一个模块包!
|
||||
|
||||
### 配置文件参数
|
||||
|
||||
#### - description
|
||||
|
||||
- 类型:`string`。
|
||||
|
||||
- 含义:模块的描述。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-first-module",
|
||||
"description": "这个是一个示例模块打包教程"
|
||||
}
|
||||
```
|
||||
|
||||
#### - version
|
||||
|
||||
- 类型:string。
|
||||
- 含义:模块的版本。
|
||||
|
||||
版本处理方式和 Composer 基本一致,建议使用三段式,也就是 `大版本.小版本.补丁版本`。关于三段式版本的描述和规范,见 [到底三段式版本号是什么?](https://www.chrisyue.com/what-the-hell-are-semver-and-the-difference-between-composer-version-control-sign-tilde-and-caret.html)。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "my-first-module",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
#### - depends
|
||||
|
||||
- 类型:map of string(例如 `{"foo":"bar","baz":"zoo"}`)。
|
||||
- 含义:模块的依赖关系和版本依赖声明。
|
||||
|
||||
此处用作模块的依赖检测,假设模块 `foo` 依赖模块 `bar` 的 1.x 版本但是不兼容 `bar` 的 2.x 版本,可以像 Composer 的 `require` 一样编写版本依赖:`^1.0`。也可以使用 `~`、`>=`、`*` 这些与 Composer 包管理相同逻辑的版本依赖关系,详见 [Composer - 包版本](https://docs.phpcomposer.com/01-basic-usage.html#Package-Versions)。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"depends": {
|
||||
"bar": "^1.0",
|
||||
"bsr": "*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### - light-cache-store
|
||||
|
||||
- 类型:array of string(例如 `["foo","bar"]`)。
|
||||
- 含义:打包模块时要储存的持久化 LightCache 键名列表。
|
||||
|
||||
这里需要配合 LightCache 使用,如果你有一些需要全局缓存的数据,例如动态配置项,比如群服务状态列表,可以先使用 LightCache 存储并使用 `addPersistence()` 持久化,此后在使用模块打包时编写此配置项。
|
||||
|
||||
我们假设在项目模块中使用到了 `group-status` 这一个 LightCache,那么只需要写 `light-cache-store` 配置项,在模块打包时就会将持久化的数据也打包到 phar 模块包内。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"light-cache-store": [
|
||||
"group-status"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### - global-config-override
|
||||
|
||||
- 类型:string | false。
|
||||
- 含义:解包时是否需要手动编辑全局配置(`global.php`)。
|
||||
|
||||
这里如果写 string 类型的,那么就是相当于在解包时会提示此处的内容,内容推荐填写要求解包模块用户需要编辑的项目,比如 「请将 static_file_server 的 status 改为 true,以便使用静态文本功能」。
|
||||
|
||||
如果是 false,那么和不指定此参数效果是一样的,无需用户修改 global.php。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"global-config-override": "请将 static_file_server 的 status 改为 true"
|
||||
}
|
||||
```
|
||||
|
||||
#### - allow-hotload
|
||||
|
||||
- 类型:bool。
|
||||
- 含义:是否允许用户无需解压直接加载模块包文件(phar)。
|
||||
|
||||
当此项为 true 时,可以将模块包直接放入 `zm_data/modules` 文件夹下,然后将 `global.php` 中的 `module_loader` 项中的 `enable_hotload` 改为 true,启动框架即可加载。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"allow-hotload": true
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning "注意"
|
||||
如果使用允许热加载,那么模块包中的配置最好不要有 `global-config-override` 和 `light-cache-store`,以此来达到最正确的效果,一般热加载更适合 Library(库)类型的模块。
|
||||
|
||||
#### - zm-data-store
|
||||
|
||||
- 类型:array of string(例如 `["foo","bar"]`)。
|
||||
- 含义:打包时要添加到模块包内的 `zm_data` 目录下的子目录或文件。
|
||||
|
||||
其中项目必须是相对路径,不能是绝对路径,且必须是在配置项 `zm_data` 指定的目录(默认会在框架项目的根目录下的 `zm_data/` 目录。
|
||||
|
||||
我们假设要打包一个 `{zm_data 目录}/config/` 目录及其目录下的文件,和一个 `main.png` 文件,下方是实例。
|
||||
|
||||
??? note "点我查看编写实例:"
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "这个是一个示例模块打包教程",
|
||||
"zm-data-store": [
|
||||
"config/",
|
||||
"main.png"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
在打包时框架会自动添加这些文件到 phar 插件包内,到解包时,会自动将这些文件释放到对应框架的 `zm_data` 目录下。
|
||||
|
||||
## 打包模块命令
|
||||
|
||||
编写配置文件 `zm.json` 后,就可以被框架正常识别为模块形式,你也可以使用对无需打包的模块进行配置以进行分类管理。
|
||||
|
||||
### module:list
|
||||
|
||||
使用 list 命令可以列出炸毛框架检测到配置文件或打包好的模块。
|
||||
|
||||
```
|
||||
$ ./zhamao module:list
|
||||
[foo]
|
||||
类型: source
|
||||
版本: 1.0.0
|
||||
描述: 示例模块打包文件
|
||||
目录: src/Module/Example
|
||||
没有发现已打包且装载的模块!
|
||||
```
|
||||
|
||||
其中 `[ ]` 内为识别出来的模块名称,由上方用户编写的 `zm.json` 定义,类型为 `source` 是源码形式,也就是指定了 `zm.json` 形式的模块,目录为模块所在子目录。
|
||||
|
||||
我们假设打包上方定义的 `foo` 模块,使用下方命令 `module:pack` 即可。
|
||||
|
||||
### module:pack
|
||||
|
||||
使用 pack 命令可以将配置好的模块打包为 `xxx.phar` 文件并转移或发布给他人。
|
||||
|
||||
我们假设打包模块脚手架的默认模块 `src/Module/Example` 下面的模块源码和附带一个 `zm_data` 目录下的文件(我们就随便打包一下 Swoole 的输出日志吧)。`zm.json` 文件内容如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "foo",
|
||||
"description": "示例模块打包文件",
|
||||
"version": "1.0.0",
|
||||
"allow-hotload": true,
|
||||
"zm-data-store": [
|
||||
"crash/swoole_error.log"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
然后输入命令:
|
||||
|
||||
```
|
||||
$ ./zhamao module:pack foo
|
||||
[15:07:11] [I] 模块输出文件:/root/zhamao-framework/zm_data/output/foo_1.0.0.phar
|
||||
[15:07:11] [S] 打包完成!
|
||||
```
|
||||
|
||||
如果提示文件夹不存在,请先手动创建文件夹:`mkdir /path/to/your/zm_data/output`
|
||||
|
||||
在打包后,你将获得一个 `foo_1.0.0.phar` 的文件。
|
||||
|
||||
> 如果你没有在 `zm.json` 中指定 `version`,那么输出的 phar 文件是不会带版本号的。
|
||||
|
||||
打包后的 phar 内将包含:
|
||||
|
||||
- Hello.php
|
||||
- zm.json
|
||||
- crash/swoole_error.log
|
||||
- 必要的框架热加载以及解包需要的配置信息
|
||||
|
||||
|
||||
## 打包命令
|
||||
|
||||
```bash
|
||||
# ./zhamao 和原先的 vendor/bin/start 是完全一致的
|
||||
./zhamao module:pack <module-name>
|
||||
```
|
||||
|
||||
例如,打包上面的名叫 foo 的模块:`./zhamao module:pack foo`。
|
||||
|
||||
打包命令执行后,将会在 `zm_data` 下的 `output` 目录输出一个 phar 文件。如果你指定了 `version` 参数,那么文件名将会是 `${name}_${version}.phar`,如果没有指定版本,那么只会有 `${name}.phar`,同时如果文件已经存在,将覆盖写入。
|
||||
|
||||
## 查看模块信息命令
|
||||
|
||||
```bash
|
||||
./zhamao module:list
|
||||
```
|
||||
|
||||
通过此命令可以查看模块相关的信息,如未打包但已配置的模块信息等。
|
||||
|
||||
84
docs/component/module/module-unpack.md
Normal file
84
docs/component/module/module-unpack.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 模块解包
|
||||
|
||||
从 2.5 版本起,炸毛框架的模块源码支持了打包和分发,分发后必不可少的一步就是将其解包。
|
||||
|
||||
解包过程大致为:
|
||||
|
||||
1. 检查模块的配置文件是否正常。
|
||||
2. 检查模块的依赖问题,如果有依赖但未安装,则抛出异常。
|
||||
3. 检查 LightCache 轻量缓存是否需要写入。
|
||||
4. 检查 `zm_data` 是否有需要存入的数据。
|
||||
5. 合并 `composer.json` 文件。
|
||||
6. 拷贝 `zm_data` 相关的文件。
|
||||
7. 写入 LightCache 相关数据。
|
||||
8. 提示用户手动合并 `global.php` 全局配置文件。
|
||||
9. 拷贝模块 PHP 源文件。
|
||||
|
||||
## 解包命令
|
||||
|
||||
```bash
|
||||
./zhamao module:unpack <module-name>
|
||||
```
|
||||
|
||||
首先将待解包的 phar 文件放入 `zm_data` 目录下的 `modules` 文件夹(如果不存在需要手动创建),如果你手动修改过 `global.php` 下面的 `module_loader.load_path` 项,需要放入对应的目录。
|
||||
|
||||
放入后,结构如下:
|
||||
|
||||
```
|
||||
zm_data/
|
||||
zm_data/modules/
|
||||
zm_data/modules/foo.phar
|
||||
```
|
||||
|
||||
接下来,需要知道模块的名称。当然一般情况下,phar 的名称可以获取到模块的实际名称,如 `foo`,但最好用 `./zhamao module:list` 列出模块的信息来获取真实的模块名称。
|
||||
|
||||
```
|
||||
./zhamao module:list
|
||||
# 下面是输出
|
||||
[foo]
|
||||
类型: 模块包(phar)
|
||||
位置: zm_data/modules/我是假的名字.phar
|
||||
```
|
||||
|
||||
解包过程十分简单,只需要执行一次命令即可。
|
||||
|
||||
```
|
||||
./zhamao module:unpack foo
|
||||
# 下面是输出
|
||||
[10:05:40] [I] Releasing source file: src/Module/Example/Hello.php
|
||||
[10:05:40] [I] Releasing source file: src/Module/Example/zm.json
|
||||
[10:05:40] [S] 解压完成!
|
||||
```
|
||||
|
||||
### 命令参数
|
||||
|
||||
在解包时会遇到各种复杂的情况,如源码文件已存在、数据已存在、依赖问题等,通过增加参数可以控制解包时的行为。
|
||||
|
||||
#### --overwrite-light-cache
|
||||
|
||||
含义:覆盖现有的 LightCache 键值(如果存在的话)。
|
||||
|
||||
#### --overwrite-zm-data
|
||||
|
||||
含义:覆盖现有的 `zm_data` 下的文件(如果存在的话)。
|
||||
|
||||
#### --overwrite-source
|
||||
|
||||
含义:覆盖现有的 PHP 模块源文件(如果存在的话)。
|
||||
|
||||
#### --ignore-depends
|
||||
|
||||
含义:解包时忽略检查依赖。
|
||||
|
||||
### 常见问题
|
||||
|
||||
如果你解包的模块包要求修改 `global.php`,则会出现类似这样的提示:
|
||||
|
||||
```
|
||||
# ./zhamao module:unpack foo
|
||||
[14:47:39] [W] 模块作者要求用户手动修改 global.php 配置文件中的项目:
|
||||
[14:47:39] [W] *请把全局配置文件的light_cache项目中max_strlen选项调整为至少65536
|
||||
请输入修改模式,y(使用vim修改)/e(自行使用其他编辑器修改后确认)/N(默认暂不修改):[y/e/N]
|
||||
```
|
||||
|
||||
一般这种情况,根据第二条提示(第二条提示为打包时填入的 `global-config-override`)。如果输入 y,则会自动执行命令 `vim config/global.php`,如果输入的是 e,则会等待你手动修改完成文件,最后按回车完成修改。默认情况直接回车的话,会跳过此步骤,如果模块要求了修改但跳过修改,安装后可能会有功能缺失等问题。
|
||||
107
docs/component/store/data-provider.md
Normal file
107
docs/component/store/data-provider.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# 存储管理(文件)
|
||||
|
||||
DataProvider 是框架内提供的一个简易的文件管理类。
|
||||
|
||||
定义:`\ZM\Utils\DataProvider`
|
||||
|
||||
## DataProvider::getWorkingDir()
|
||||
|
||||
同 `working_dir()`。
|
||||
|
||||
## DataProvider::getSourceRootDir()
|
||||
|
||||
获取用户的源码根目录,除 Phar 模式外与 `getWorkingDir()` 相同。
|
||||
|
||||
## DataProvider::getFrameworkLink()
|
||||
|
||||
同 `ZMConfig::get("global", "http_reverse_link")`,获取反向代理的链接。
|
||||
|
||||
## DataProvider::getDataFolder()
|
||||
|
||||
获取配置项 `zm_data` 指定的目录。
|
||||
|
||||
如果指定参数 `$second`,则返回二级目录地址,如果二级目录不存在则自动创建。
|
||||
|
||||
```php
|
||||
DataProvider::getDataFolder("TestModule"); // 例如返回 /root/zhamao-framework/zm_data/TestModule/
|
||||
```
|
||||
|
||||
## DataProvider::getFrameworkRootDir()
|
||||
|
||||
返回框架本体的根目录。
|
||||
|
||||
## 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 解析失败)。
|
||||
|
||||
## DataProvider::scanDirFiles()
|
||||
|
||||
递归或非递归扫描目录,返回相对目录的文件列表或绝对目录的文件列表。(非常好用)
|
||||
|
||||
定义:`scanDirFiles($dir, $recursive = true, $relative = false)`
|
||||
|
||||
`$dir` 为要扫描的目录,`$recursive` 为是否递归,`$relative` 为是否返回相对目录的文件列表。
|
||||
|
||||
从给定的目录下开始遍历整个目录,如果将 `$recursive` 设置为 `true`,则会递归扫描子目录,否则将返回包含目录的文件列表。
|
||||
|
||||
如果将 `$relative` 设置为 `true`,则会返回文件列表的相对路径,否则返回绝对路径。
|
||||
|
||||
例如:假设目录 `/home/test/` 下有两个文件:`test1.txt` 和 `testdir/test2.txt`:如果将 `$recursive` 设置为 `true`,`$relative` 设置为 `false`,则返回的文件列表为:
|
||||
|
||||
```json
|
||||
[
|
||||
"/home/test/test1.txt",
|
||||
"home/test/testdir/test2.txt"
|
||||
]
|
||||
```
|
||||
|
||||
相同条件下,如果将 `$relative` 设置为 `true`:
|
||||
|
||||
```json
|
||||
[
|
||||
"test1.txt",
|
||||
"testdir/test2.txt"
|
||||
]
|
||||
```
|
||||
|
||||
如果再把 `$recursive` 设置为 `false`:
|
||||
|
||||
```json
|
||||
[
|
||||
"test1.txt",
|
||||
"testdir"
|
||||
]
|
||||
```
|
||||
|
||||
## 其他文件读取
|
||||
|
||||
框架比较贴近原生的 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()` 获取目录后面需要加 `/` 再追加自己的文件名或子目录名。
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
```php
|
||||
/** 轻量字符串缓存,默认开启 */
|
||||
$config['light_cache'] = [
|
||||
'size' => 1024, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 16384, //单行字符串最大长度(需要2的倍数)
|
||||
'size' => 512, //最多允许储存的条数(需要2的倍数)
|
||||
'max_strlen' => 32768, //单行字符串最大长度(需要2的倍数)
|
||||
'hash_conflict_proportion' => 0.6, //Hash冲突率(越大越好,但是需要的内存更多)
|
||||
'persistence_path' => $config['zm_data'].'_cache.json',
|
||||
'auto_save_interval' => 900
|
||||
@@ -57,7 +57,9 @@ $config['light_cache'] = [
|
||||
|
||||
`$value` 可存入 `bool`、`string`、`int`、`array` 等可被 `json_encode()` 的变量,闭包函数和对象不可存入。
|
||||
|
||||
`$expire` 是 `int`,超时时间(秒)。如果设定了大于 0 的值,则表明是在 `$expire` 秒后自动删除。如果为 -1 则什么都不做,如果框架使用了 `stop` 或 Ctrl+C 或意外退出时数据会丢失。如果为 -2,则会将此数据持久化保存,保存在上方配置文件指定的 json 文件中,待关闭后再次启动框架会自动加载回来,不会丢失。
|
||||
`$expire` 是 `int`,超时时间(秒)。如果设定了大于 0 的值,则表明是在 `$expire` 秒后自动删除(框架中途停止不受影响)。如果为 -1 则什么都不做。框架停止后自动被清除。
|
||||
|
||||
**注意:如果前面使用了 set() ,后面再次使用 set() 会重置 expire 过期时间为 -1(-1 是框架运行时不过期,关闭框架删除的状态),如果只需要更新值,请使用 update()。**
|
||||
|
||||
```php
|
||||
// use ZM\Store\LightCache;
|
||||
@@ -88,6 +90,14 @@ public function storeAfterRemove() {
|
||||
( 内容不存在!
|
||||
</chat-box>
|
||||
|
||||
### LightCache::update()
|
||||
|
||||
更新值而不更新状态。如果键值对不存在,则返回 false。
|
||||
|
||||
定义:`LightCache::update(string $key, $value)`
|
||||
|
||||
参数同 `set()`,可参考。
|
||||
|
||||
### LightCache::get()
|
||||
|
||||
获取内容。
|
||||
@@ -106,12 +116,26 @@ zm_sleep(10);
|
||||
dump(LightCache::getExpire("test")); // 返回 10
|
||||
```
|
||||
|
||||
### LightCache::getExpireTS()
|
||||
|
||||
获取存储项要过期的时间戳。(2.4.3 起可用)
|
||||
|
||||
定义:`LightCache::getExpireTS(string $key)`
|
||||
|
||||
```php
|
||||
$s = LightCache::set("test", "hello", 20); //假设这条代码执行时时间戳是 1616838482
|
||||
zm_sleep(10);
|
||||
dump(LightCache::getExpireTS("test")); // 返回 1616838502
|
||||
zm_sleep(10);
|
||||
dump(LightCache::getExpireTS("test")); // 返回 null
|
||||
```
|
||||
|
||||
### LightCache::getMemoryUsage()
|
||||
|
||||
获取轻量缓存使用的总空间大小(字节)
|
||||
|
||||
```php
|
||||
LightCache::getMemoryUsage());
|
||||
LightCache::getMemoryUsage();
|
||||
```
|
||||
|
||||
轻量缓存的内存手工计算方式:(Table 结构体长度` + `KEY 长度 64 字节 + `$size`) * (1 + `$conflict_proportion`) * 列尺寸。
|
||||
@@ -157,25 +181,34 @@ dump(LightCache::getAll());
|
||||
*/
|
||||
```
|
||||
|
||||
### LightCache::savePersistence()
|
||||
### LightCache::addPersistence()
|
||||
|
||||
立刻保存所有被标记为持久化的缓存项到磁盘。
|
||||
添加持久化存储的键。
|
||||
|
||||
!!! note "提示"
|
||||
用法:`LightCache::addPersistence($key)`。
|
||||
|
||||
在一般情况下,框架定时执行此方法来保存,在停止框架、reload 框架和 Ctrl+C 停止框架的时候,均会执行保存。
|
||||
注:只需调用一次即可,无需多次重复调用,也不需要设置 expire 为 -2 了。(2.4.2 起可用此方法)。
|
||||
|
||||
详见下方 **持久化**。
|
||||
|
||||
### LightCache::removePersistence()
|
||||
|
||||
删除持久化的键。
|
||||
|
||||
用法:`LightCache::removePersistence($key)`。
|
||||
|
||||
注:只需调用一次即可,无需多次重复调用,也不需要设置 expire 为非 -2 了。(2.4.2 起可用此方法)。
|
||||
|
||||
### 持久化
|
||||
|
||||
将 `set()` 的 expire 设置为 -2 即可。
|
||||
使用 `LightCache::addPersistence($key)` 添加对应需要持久化的键名即可。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQCommand("store")
|
||||
* @OnStart()
|
||||
*/
|
||||
public function store() {
|
||||
LightCache::set("msg_time", time(), -2);
|
||||
return "OK!";
|
||||
public function onStart() {
|
||||
LightCache::addPersistence("msg_time");
|
||||
}
|
||||
/**
|
||||
* @CQCommand("getStore")
|
||||
@@ -187,11 +220,11 @@ public function getStore() {
|
||||
|
||||
<chat-box>
|
||||
^ 我在 2021-01-05 15:21:00 发送这条消息
|
||||
) store
|
||||
( OK!
|
||||
) getStore
|
||||
( 2021-01-05 15:20:00
|
||||
^ 这时我用 Ctrl+C 停止框架,过一会儿再启动
|
||||
) getStore
|
||||
( 存储时间:2021-01-05 15:21:00
|
||||
( 存储时间:2021-01-05 15:20:00
|
||||
</chat-box>
|
||||
|
||||
### 数据加锁
|
||||
@@ -292,7 +325,7 @@ class Hello {
|
||||
* @CQCommand("set_store")
|
||||
*/
|
||||
public function setStorage() {
|
||||
$arg1 = ctx()->getFullArg("请输入要设置的内容名称");
|
||||
$arg1 = ctx()->getNextArg("请输入要设置的内容名称");
|
||||
$arg2 = ctx()->getFullArg("请输入要设置的内容");
|
||||
WorkerCache::set($arg1, $arg2);
|
||||
return "成功!";
|
||||
@@ -1,4 +1,8 @@
|
||||
# MySQL 数据库
|
||||
# MySQL 数据库(旧版组件)
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
此 MySQL 组件为旧版 MySQL 查询器组件,为了统一和提升对未来独立组件的兼容性,现转变为使用 `doctrine/dbal` 和 `doctrine/orm` 库来实现查询器,请转到 [MySQL 查询器]()。
|
||||
|
||||
## 配置
|
||||
|
||||
184
docs/component/store/mysql/common-query.md
Normal file
184
docs/component/store/mysql/common-query.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# 执行 SQL 语句
|
||||
|
||||
在一开始,无论你做什么数据库操作,均需要获取一个 `\ZM\MySQL\MySQLWrapper` 作为你的操作对象。
|
||||
|
||||
```php
|
||||
/** @var \ZM\MySQL\MySQLWrapper $wrapper */
|
||||
$wrapper = \ZM\MySQL\MySQLManager::getWrapper();
|
||||
```
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
这部分内容部分直接取自 [DBAL - Data Retrieval And Manipulation](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.13/reference/data-retrieval-and-manipulation.html) 原文并直接翻译,如有实际不同请提交 Issue 反馈。
|
||||
|
||||
## 执行预处理 SQL 语句
|
||||
|
||||
预处理查询很巧妙地解决了 SQL 注入问题,并且可以方便地绑定参数进行查询。
|
||||
|
||||
预处理一般是指使用 `?` 占位符或 `:xxx` 命名标签进行参数留空,先处理 SQL 语句再填入数据。
|
||||
|
||||
一般 `?` 具有前后位置性,例如如下的查询:
|
||||
|
||||
```php
|
||||
$sql = "SELECT * FROM users WHERE id = ? AND username = ?";
|
||||
$stmt = $wrapper->getConnection()->prepare($sql);
|
||||
$stmt->bindValue(1, "1");
|
||||
$stmt->bindValue(2, "jack");
|
||||
$resultSet = $stmt->executeQuery();
|
||||
```
|
||||
|
||||
其中 `$resultSet` 与 `Statement` 方法相似,此处的对象可能是 [数据库语句对象](../mysql-statement) 或 数据库结果对象(结果对象与语句对象的 `fetchXXX()` 部分一致)。
|
||||
|
||||
这里也可以使用命名标签,使用标签可以给相同参数处使用同一个标签:
|
||||
|
||||
```php
|
||||
$sql = "SELECT * FROM users WHERE gender = :name OR username = :name";
|
||||
$stmt = $wrapper->getConnection()->prepare($sql);
|
||||
$stmt->bindValue("name", "jack");
|
||||
$resultSet = $stmt->executeQuery();
|
||||
```
|
||||
|
||||
## 执行常规语句
|
||||
|
||||
执行常规语句为 `statement` 方式执行,此方法执行后只返回影响的行数,而不返回结果,适用于 `UPDATE` 等语句。
|
||||
|
||||
```php
|
||||
<?php
|
||||
$count = $wrapper->executeStatement('UPDATE users SET username = ? WHERE id = ?', array('jwage', 1));
|
||||
echo $count; // 1
|
||||
```
|
||||
|
||||
## 执行查询语句
|
||||
|
||||
为给定的 SQL 创建一个准备好的语句并将参数传递给 executeQuery 方法,然后返回结果集。此方法为上述的「预处理查询语句」的简化版,可直接在第二个参数使用 array 插入绑定参数执行。
|
||||
|
||||
```php
|
||||
$resultSet = $wrapper->executeQuery('SELECT * FROM user WHERE username = ?', array('jack'));
|
||||
$user = $resultSet->fetchAssociative();
|
||||
|
||||
/* $user 值
|
||||
array(
|
||||
0 => array(
|
||||
'id' => 1,
|
||||
'username' => 'jack',
|
||||
'gender' => 'man',
|
||||
'update_time' => '2021-10-12'
|
||||
)
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchAllAssociative()
|
||||
|
||||
执行查询并将所有结果返回一个数组中。
|
||||
|
||||
因此,上面的查询语句还可以直接被简化为一次方法调用:
|
||||
|
||||
```php
|
||||
$resultSet = $wrapper->fetchAllAssociative('SELECT * FROM user WHERE username = ?', array('jack'));
|
||||
// 结果同 executeQuery()->fetchAllAssociative() 中 $user 的值。
|
||||
```
|
||||
|
||||
### fetchAllKeyValue()
|
||||
|
||||
执行查询并将前两列分别作为键和值提取到关联数组中。
|
||||
|
||||
```php
|
||||
$resultSet = $wrapper->fetchAllKeyValue('SELECT username, gender FROM user WHERE username = ?', array('jack'));
|
||||
|
||||
/* $resultSet 值
|
||||
array(
|
||||
'jack' => 'man'
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchAllAssociativeIndexed()
|
||||
|
||||
执行查询并将数据作为关联数组获取,其中键代表第一列,值是其余列及其值的关联数组。
|
||||
|
||||
```php
|
||||
$users = $wrapper->fetchAllAssociativeIndexed('SELECT id, username, gender FROM users');
|
||||
|
||||
/*
|
||||
array(
|
||||
1 => array(
|
||||
'username' => 'jack',
|
||||
'gender' => 'man',
|
||||
'update_time' => '2021-10-12'
|
||||
)
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchNumeric()
|
||||
|
||||
查询并返回第一行数据,形式以数字索引方式返回每一列。
|
||||
|
||||
```php
|
||||
$user = $wrapper->fetchNumeric('SELECT * FROM users WHERE username = ?', array('jack'));
|
||||
|
||||
/*
|
||||
array(
|
||||
0 => 'jwage',
|
||||
1 => 'man',
|
||||
2 => '2021-10-12'
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### fetchOne()
|
||||
|
||||
仅返回查询结果的第一行第一列的值。
|
||||
|
||||
```php
|
||||
$username = $wrapper->fetchOne('SELECT username FROM users WHERE id = ?', array(1));
|
||||
echo $username; // jack
|
||||
```
|
||||
|
||||
### fetchAssociative()
|
||||
|
||||
返回结果内第一行的关联数组形式的数据。
|
||||
|
||||
```php
|
||||
$users = $wrapper->fetchAssociative('SELECT * FROM users');
|
||||
|
||||
/*
|
||||
array(
|
||||
'id' => 1,
|
||||
'username' => 'jack',
|
||||
'gender' => 'man',
|
||||
'update_time' => '2021-10-12'
|
||||
)
|
||||
*/
|
||||
```
|
||||
|
||||
### delete()
|
||||
|
||||
删除查询操作,第一个参数为表名,第二个参数为 `['列名' => '列值']`。
|
||||
|
||||
```php
|
||||
<?php
|
||||
$wrapper->delete('users', array('username' => 'jack'));
|
||||
// 等同于执行DELETE FROM user WHERE username = ? ,参数列表为('jack')
|
||||
```
|
||||
|
||||
### insert()
|
||||
|
||||
插入数据库一行,第一个参数为表名,第二个参数为对应数据。
|
||||
|
||||
```php
|
||||
$wrapper->insert('users', array('id' => 0, 'username' => 'jwage', 'gender' => 'woman', 'update_time' => '2021-10-17'));
|
||||
// INSERT INTO user (id, username, gender, update_time) VALUES (?,?,?,?) (0,jwage,woman,2021-10-17)
|
||||
```
|
||||
|
||||
### update()
|
||||
|
||||
更新数据库,使用给定数据更新匹配键值标识符的所有行。
|
||||
|
||||
```php
|
||||
<?php
|
||||
$wrapper->update('user', array('username' => 'jwage'), array('id' => 1));
|
||||
// UPDATE user (username) VALUES (?) WHERE id = ? (jwage, 1)
|
||||
```
|
||||
|
||||
7
docs/component/store/mysql/config.md
Normal file
7
docs/component/store/mysql/config.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 配置
|
||||
|
||||
炸毛框架的数据库组件支持原生 SQL、查询构造器,去掉了复杂的对象模型关联,同时默认为数据库连接池,使开发变得简单。
|
||||
|
||||
数据库的配置位于 `config/global.php` 文件的 `mysql_config` 段,见 [全局配置](../../../../guide/basic-config#mysql_config)。
|
||||
|
||||
如果 `mysql_config.host` 字段为空,则不创建数据库连接池,填写后将创建,且默认保持长连接。
|
||||
1
docs/component/store/mysql/mysql-statement.md
Normal file
1
docs/component/store/mysql/mysql-statement.md
Normal file
@@ -0,0 +1 @@
|
||||
你好啊,这里是 Statement。
|
||||
14
docs/component/store/mysql/mysql.md
Normal file
14
docs/component/store/mysql/mysql.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# MySQL 数据库简介
|
||||
|
||||
炸毛框架的数据库组件对接了 MySQL 连接池,在使用过程中无需配置即可实现 MySQL 查询,同时拥有高并发。
|
||||
|
||||
目前 2.5 版本后炸毛框架底层采用了 `doctrine/dbal` 组件,可以方便地构建 SQL 语句。
|
||||
|
||||
本章大体查询内容均以下表 `users` 为基础:
|
||||
|
||||
| id | username | gender | update_time |
|
||||
| -- | -------- | ------ | ----------- |
|
||||
| 1 | jack | man | 2021-10-12 |
|
||||
| 2 | rose | woman | 2021-10-11 |
|
||||
|
||||
#
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
前面讲到 LightCache 轻量缓存在特定的情况下为了保证数据不被多进程的因素导致丢失或覆盖,在高并发情况下修改数据需要加锁,所以炸毛框架内置了 SpinLock 自旋锁。
|
||||
|
||||
!!! tip "提示"
|
||||
|
||||
框架单进程运行的模式下不需要任何自旋锁。
|
||||
|
||||
## 配置
|
||||
|
||||
自旋锁使用无需配置,和 LightCache 同源。
|
||||
@@ -31,7 +35,7 @@ SpinLock::unlock("foo");
|
||||
给信号量 `$key` 上锁。如果该信号量已经被上锁,则立刻返回 false。
|
||||
|
||||
```php
|
||||
SpinLock::lock("foo");
|
||||
SpinLock::trylock("foo");
|
||||
```
|
||||
|
||||
## 综合实例
|
||||
@@ -70,4 +74,4 @@ public function test() {
|
||||
|
||||
## 性能
|
||||
|
||||
使用自旋锁几乎没有性能损失,自旋锁要比其他类型的锁性能强很多,在上方举例使用的 `ab` 压测工具测试 100万请求 下,使用自旋锁和不适用自旋锁的测试成绩时间分别为:7.4s 和 6.9s。
|
||||
使用自旋锁几乎没有性能损失,自旋锁要比其他类型的锁性能强很多,在上方举例使用的 `ab` 压测工具测试 100万请求 下,使用自旋锁和不适用自旋锁的测试成绩时间分别为:7.4s 和 6.9s。
|
||||
@@ -1,23 +0,0 @@
|
||||
# ZMUtil 杂项工具类
|
||||
|
||||
调用前先 use:`use ZM\Utils\ZMUtil;`
|
||||
|
||||
## ZMUtil::stop()
|
||||
|
||||
停止框架运行。
|
||||
|
||||
## ZMUtil::reload()
|
||||
|
||||
重载框架,这会断开所有到框架的连接和重载所有 `src/` 目录下的用户源码并重新加载所有 Worker 进程。
|
||||
|
||||
## ZMUtil::getModInstance()
|
||||
|
||||
根据类名称拿到此类的单例(前提是目标的类的构造函数为空)。
|
||||
|
||||
```php
|
||||
class ASD{
|
||||
public $test = 0;
|
||||
}
|
||||
ZMUtil::getModInstance(ASD::class)->test = 5;
|
||||
```
|
||||
|
||||
@@ -102,15 +102,15 @@ class Test {
|
||||
|
||||
在炸毛框架内部,一个完整的事件流程和中间件的关系如下图:
|
||||
|
||||

|
||||

|
||||
|
||||
对于同一事件的优先级和响应顺序,优先级的关系如下图:
|
||||
|
||||

|
||||

|
||||
|
||||
对于事件内单个事件被调用的单个函数下如果存在多个中间件,中间件模型和事件的关系如下图:
|
||||
|
||||

|
||||

|
||||
|
||||
## 实战例子
|
||||
|
||||
|
||||
@@ -221,6 +221,27 @@
|
||||
| tick_ms | `int`,**必填**,间隔的毫秒数,例如 1 秒间隔为 `1000`,范围大于 0,小于 86400000。 | | |
|
||||
| worker_id | `int`,要在哪个 Worker 进程上执行,默认为 0,范围是 0~{你设定的 Worker 数量-1},如果是 -1 的话,则会在所有 Worker 进程上触发。 | 限定只执行的 Worker 进程 | |
|
||||
|
||||
## OnTask()
|
||||
|
||||
定义一个在工作进程中运行的任务函数。详情见 [进阶 - 使用 TaskWorker 进程处理密集运算](/advanced/task-worker)。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | ----------------------------- |
|
||||
| 名称 | `@OnTask` |
|
||||
| 触发前提 | 在框架加载后激活 |
|
||||
| 命名空间 | `ZM\Annotation\Swoole\OnTask` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 有,返回 Worker 进程的结果 |
|
||||
|
||||
### 注解参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| --------- | ------------------------------------------------------------ | ------------ | ---- |
|
||||
| task_name | `string`,**必填**,任务函数的名称,不建议重复。 | | |
|
||||
| rule | 设置触发前提,PHP 代码,返回 bool 值即可,参考 OnRequestEvent | 限定是否执行 | 空 |
|
||||
|
||||
## OnSetup()
|
||||
|
||||
在框架加载前执行的代码。此部分代码是在主进程执行的,不可在此事件中使用任何协程相关的功能。
|
||||
@@ -241,12 +262,34 @@
|
||||
|
||||
无。
|
||||
|
||||
## TerminalCommand()
|
||||
|
||||
添加一个远程终端的自定义命令。(2.4.0 版本起可用)
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ---------- | --------------------------------------- |
|
||||
| 名称 | `@TerminalCommand` |
|
||||
| 触发前提 | 连接到远程终端可触发 |
|
||||
| 命名空间 | `ZM\Annotation\Command\TerminalCommand` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 无 |
|
||||
|
||||
### 注解参数
|
||||
|
||||
| 参数名称 | 参数范围 | 默认 |
|
||||
| ----------- | ------------------------------ | ---- |
|
||||
| command | `string`,**必填**,命令字符串 | |
|
||||
| alias | `string`,可选,命令的别名 | |
|
||||
| description | `string`,要显示的帮助文本 | 空 |
|
||||
|
||||
## 示例1(机器人连接框架后输出信息)
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
class Hello {
|
||||
@@ -385,3 +428,6 @@ public function onCrawl() {
|
||||
}
|
||||
```
|
||||
|
||||
## 示例6(创建一个远程终端命令并调试框架)
|
||||
|
||||
> 开个坑,以后填。(__填坑标记__)
|
||||
@@ -163,5 +163,33 @@ public function onThrowing(?Exception $e) {
|
||||
|
||||
这里的 `@HandleException` 中的参数为要捕获的类名,注意这里面的类名的命名空间需要写全称,不能上面 use 再使用,否则会无法找到异常类。
|
||||
|
||||
`context()` 为获取当前协程空间绑定的 `request` 和 `response` 对象。
|
||||
`ctx()` 为获取当前协程空间绑定的 `request` 和 `response` 对象。
|
||||
|
||||
## 中间件加载错误处理策略
|
||||
|
||||
中间件在某些情况下可能会产生普通 PHP 异常以外的异常,不能被框架的正常错误流程捕获,所以这里额外说明了中间件异常处理的几种策略。
|
||||
|
||||
中间件异常处理策略可以在 2.5.2 版本之后通过 `global.php` 中的 `runtime` 下 `middleware_error_policy` 设置。
|
||||
|
||||
- **0**: 无论被运行事件中间件是否存在,都不抛出异常,继续执行事件。
|
||||
- **1**: 在框架启动时如果某事件被注解了一个不存在的中间件,则不抛出异常,在执行期间才检测是否存在此中间件,并抛出异常。
|
||||
- **2**: 严格的中间件检查,在框架启动时就检测所有被注解了中间件的注解事件。
|
||||
|
||||
假设我们有一个路由注解 `@RequestMapping("/test")`,同时注解了一个不存在的中间件,如下:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/test")
|
||||
* @Middleware("foo")
|
||||
*/
|
||||
public function testRoute() {
|
||||
return "I am testing middleware";
|
||||
}
|
||||
```
|
||||
|
||||
配置项为 0,此中间件类不存在的话,则会报告 warning,并直接执行此函数。
|
||||
|
||||
配置项为 1,在访问此路由执行此函数时会抛出异常,中断此次事件。
|
||||
|
||||
配置项为 2,在框架启动时抛出致命异常。
|
||||
|
||||
|
||||
@@ -11,10 +11,25 @@ QQ 机器人事件是指 CQHTTP 插件发来的 Event 事件,被框架处理
|
||||
事件是用户需要从 OneBot 被动接收的数据,有以下几个大类:
|
||||
|
||||
- [消息事件](#cqmessage),包括私聊消息、群消息等,被 [`@CQCommand`](#cqcommand),`@CQMessage` 注解处理。
|
||||
|
||||
- [通知事件](#cqnotice),包括群成员变动、好友变动等,被 `@CQNotice` 注解事件处理。
|
||||
|
||||
- [请求事件](#cqrequest),包括加群请求、加好友请求等,被 `@CQRequest` 注解事件处理。
|
||||
|
||||
- [元事件](#cqmetaevent),包括 OneBot 生命周期、心跳等,被 `@CQMetaEvent` 注解事件处理。
|
||||
|
||||
## 注解事件参照表
|
||||
|
||||
| 注解名称 | 类所在命名全称 | 作用 |
|
||||
| ------------------------------------------------------- | ---------------------------- | ------------------------------------------------------------ |
|
||||
| [`@CQBefore`](/event/robot-annotations/#cqbefore) | `\ZM\Annotation\CQBefore` | OneBot 各类事件前触发的,可当作事件过滤器使用 |
|
||||
| [`@CQAfter`](/event/robot-annotations/#cqafter) | `\ZM\Annotation\CQAfter` | OneBot 各类事件后触发的 |
|
||||
| [`@CQMessage`](/event/robot-annotations/#cqmessage) | `\ZM\Annotation\CQMessage` | OneBot 中消息类事件的触发(机器人消息)事件 |
|
||||
| [`@CQCommand`](/event/robot-annotations/#cqcommand) | `\ZM\Annotation\CQCommand` | OneBot 中消息类事件的触发(机器人消息)事件,但是被封装为指令型的,无需自己切割命令式 |
|
||||
| [`@CQNotice`](/event/robot-annotations/#cqnotice) | `\ZM\Annotation\CQNotice` | OneBot 中通知类事件的触发(机器人消息)事件 |
|
||||
| [`@CQRequest`](/event/robot-annotations/#cqrequest) | `\ZM\Annotation\CQRequest` | OneBot 中请求类事件的触发(机器人消息)事件,一般带有请求信息,可联动相关响应的 API 完成功能编写 |
|
||||
| [`@CQMetaEvent`](/event/robot-annotations/#cqmetaevent) | `\ZM\Annotation\CQMetaEvent` | OneBot 中涉及 OneBot 实现本身的一些和机器人事件无关的元事件,比如 WS 连接的心跳包 |
|
||||
|
||||
## CQMessage()
|
||||
|
||||
QQ 收到消息后触发的事件对应注解。
|
||||
@@ -318,6 +333,8 @@ TODO:先放着,有时间再更。
|
||||
在设置了 `level` 参数后,如果设置了多个 `@CQBefore` 监听事件函数,更高 `level` 的事件函数返回了 `false`,则低 `level` 的绑定函数不会执行,所有 `@CQMessage` 绑定的事件也不会执行。
|
||||
|
||||
你也可以使用 `@CQBefore` 做一些消息的转发和过滤。比如你想去除用户发来的文字中的 emoji、图片等 CQ 码,只保留文本。
|
||||
|
||||
使用 `ctx()->waitMessage()` 时等待用户输入下一条消息功能和 CQBefore 配合过滤消息时需注意,见 [FAQ - CQBefore 过滤不了 waitMessage](/FAQ/wait-message-cqbefore/)
|
||||
|
||||
## CQAfter()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
!!! quote "开发提示"
|
||||
|
||||
本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不罗嗦。
|
||||
本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不啰嗦。
|
||||
|
||||
## 控制器和路由
|
||||
|
||||
@@ -228,4 +228,4 @@ public function staticImage($param) {
|
||||
}
|
||||
```
|
||||
|
||||
这样当用户访问 `http://框架地址/images/aaa.jpg` 就可以快速地调用此路由下的局部文件服务器功能了。
|
||||
这样当用户访问 `http://框架地址/images/aaa.jpg` 就可以快速地调用此路由下的局部文件服务器功能了。
|
||||
|
||||
7
docs/faq/FAQ.md
Normal file
7
docs/faq/FAQ.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# FAQ
|
||||
|
||||
这里会写一些常见的疑难解答,点击左侧问题名称打开对应解决方法。
|
||||
|
||||
如果框架运行过程中发现带有错误码(如 `E00034` 的形式),可以到 [错误码](/guide/errcode) 查看。
|
||||
|
||||
框架的常见问题见 [常见问题汇总](/faq/usual-question)。
|
||||
19
docs/faq/address-already-in-use.md
Normal file
19
docs/faq/address-already-in-use.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 启动时报错 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 看不到框架相关进程,证明关闭成功,否则需要使用第一条强行杀死
|
||||
```
|
||||
22
docs/faq/display-deadlock.md
Normal file
22
docs/faq/display-deadlock.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 出现 deadlock 字样
|
||||
|
||||
一般情况下,如果误操作框架可能会报如下图的错误:
|
||||
|
||||
```
|
||||
===================================================================
|
||||
[FATAL ERROR]: all coroutines (count: 1) are asleep - deadlock!
|
||||
===================================================================
|
||||
|
||||
[Coroutine-1]
|
||||
--------------------------------------------------------------------
|
||||
#0 Swoole\Coroutine\System::sleep() called at [/Users/jerry/project/git-project/zhamao-framework/src/ZM/global_functions.php:232]
|
||||
#1 zm_sleep() called at [/Users/jerry/project/git-project/zhamao-framework/src/Module/Example/Hello.php:38]
|
||||
#2 Module\Example\Hello->onStart() called at [/Users/jerry/project/git-project/zhamao-framework/src/ZM/Event/EventDispatcher.php:205]
|
||||
#3 ZM\Event\EventDispatcher->dispatchEvent() called at [/Users/jerry/project/git-project/zhamao-framework/src/ZM/Event/EventDispatcher.php:89]
|
||||
#4 ZM\Event\EventDispatcher->dispatchEvents() called at [/Users/jerry/project/git-project/zhamao-framework/src/ZM/Event/SwooleEvent/OnWorkerStart.php:130]
|
||||
#5 ZM\Event\SwooleEvent\OnWorkerStart->onCall() called at [/Users/jerry/project/git-project/zhamao-framework/src/ZM/Framework.php:336]
|
||||
```
|
||||
|
||||
这种错误的出现原因一般是因为协程未结束而 Worker 进程提前退出导致的,这个错误也可手动造成(在任意 Worker 进程内的位置使用 `zm_yield()` 且不使用 `zm_resume()` 恢复,期间使用 reload 或 stop 重启或停止框架就会报错)。
|
||||
|
||||
还有一种情况是数据库、文件读取或下载上传还没有传送结束,时间已经超时,在关闭或重启框架时不得不强行切断协程的运行。这种情况建议根据下方的打印输出栈进行插错,建议将协程运行时间长的过程缩短或调长 `swoole` 配置项下面的 `max_wait_time` 时间(秒),2.4.3 版本起此参数默认为 5 秒。
|
||||
5
docs/faq/light-cache-wrong.md
Normal file
5
docs/faq/light-cache-wrong.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 使用 LightCache 关闭时无法正常保存持久化
|
||||
|
||||
LightCache 因为是跨内存使用的,所以每次重启和关闭框架时,都只会让其中一个进程去保存。因为在 2.4.2 版本开始,持久化的逻辑发生了更改,不再支持 `expire = -2` 进行设置持久化(因为那样会很容易让开发者写错),仅支持使用 `LightCache::addPersistence($key)` 这样的方式进行设置持久化,所以在 2.4.2 版本以后,请使用此方法进行持久化设置,保证数据不丢失。
|
||||
|
||||
此外,2.4.2 版本起,不再支持用户手动调用 `savePersistence()` 方法,普通用户不可手动调用此方法,否则会导致数据出错。
|
||||
66
docs/faq/usual-question.md
Normal file
66
docs/faq/usual-question.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 框架常见问题(持续更新)
|
||||
|
||||
## 如何正确地强制退出炸毛框架?
|
||||
|
||||
首先要知道一个概念,炸毛框架和传统的 PHP 以及其他如 Python 等语言的轻量框架都不同,框架启动后会依次启动 Master、Manager、Worker 等多个进程,而用户启动时入口的 PHP 进程就是 Master 进程,在一些对框架的正常中止、热重启上,我们给 Master 进程发送相应的 Linux 信号(如 SIGTERM)即可对整个框架的多个进程生效,无需给每个进程发送。
|
||||
|
||||
但是如果因为用户的误操作,导致炸毛框架其中的一个或多个进程阻塞,或者比如将框架挂在 screen 等守护但是守护服务进程被杀掉,总之就是无法使用 Ctrl+C 的方式正常关闭框架,这时就需要正确地杀掉所有框架进程(这固然可能会造成内存的缓存数据丢失)。
|
||||
|
||||
### v2.7.0 及以上版本教程
|
||||
|
||||
- 安全关框架指令:`./zhamao server:stop`
|
||||
- 万能杀死所有框架进程指令:`./zhamao server:stop --force`
|
||||
- 监视框架是否在运行:`./zhamao server:status`
|
||||
- Worker 进程卡死:连续按 5 次 Ctrl+C 即可强行杀掉所有进程(SIGKILL)
|
||||
|
||||
### v2.6.6 及以下版本教程
|
||||
|
||||
!!! warning "注意"
|
||||
|
||||
下方涉及 `ps` 命令后使用 `grep` 过滤的框架进程方式,如果你的服务器同时有其他使用 PHP 启动的服务,命令行刚好有 `server` 字样,可能会导致误杀,如果有影响的话,建议将 `grep server` 换成你启动时命令行的特殊参数或手动排除!
|
||||
|
||||
**一、**首先,使用 `ps`、`htop`、`netstat -nlp` 等命令确定框架的入口进程(也就是 Master 进程的 pid)。
|
||||
|
||||
确认方式示例如下:
|
||||
|
||||
- 如果你使用的是 >=2.4 版本的框架,在框架启动时就会在最先开始的 motd 上方显示 `master_pid`,如果你还能找到此处的显示,那么恭喜你,可以直接进行下面的第二步。
|
||||
- 如果你不能正常通过框架的方式找到 pid,可以通过命令 `ps aux | grep php | grep server` 的方式找到框架所有的进程。其中列出的相关框架的进程,可以寻找 pid 最小的进程,即为 Master 进程。关于如何区分进程对应关系,见本页 [使用 Linux 工具辨别框架进程]()。
|
||||
- 如果你对 `ps` 不熟悉,可以使用 `htop` 工具,使用 `F5 Tree` 方式显示,并且使用 `F4` 的 Filter,过滤 `php` 或 `bin/start` 等字样,找到进程树。
|
||||
|
||||
**二、**然后,确定框架是否正常运行且正常流程关闭。
|
||||
|
||||
如果框架能正常运行,比如可以通过访问浏览器的 `http://地址:端口/httpTimer` 等 HTTP 路由,可以使用 `SIGINT` 或 `SIGTERM` 信号正常关闭框架。我们假设 Master 进程的 pid 为 31234:`kill -TERM 31234` 或 `kill -INT 31234`,如果稍后使用 `ps aux | grep php | grep server` 命令发现没有进程存在(排除掉 grep 自身的进程),说明可以正常关闭,此关闭方法为正常停止流程,即保存了 `LightCache` 等内存缓存持久化的数据。
|
||||
|
||||
如果以上方式没有任何效果,继续看第三步。
|
||||
|
||||
**三、**不能正常流程关闭,需要手动杀掉所有进程。
|
||||
|
||||
首先使用 `ps aux | grep php | grep server | grep -v grep | awk '{print $2}'` 列出框架所有进程的 pid,确认无误后,在此条命令后接 `| xargs kill -9` 即可:
|
||||
|
||||
```bash
|
||||
# 列出进程,只显示包含php,只显示包含server,排除grep本身进程,显示第二列的pid,使用xargs循环kill这里面的进程
|
||||
ps aux | grep php | grep server | grep -v grep | awk '{print $2}' | xargs kill -9
|
||||
```
|
||||
|
||||
## 如何使用 Linux 工具查看框架进程状态?
|
||||
|
||||
框架有多个进程,有时候我们需要通过监视进程状态来确定框架是否正常运行或查看框架的资源占用率。首先一个大概念,老生常谈,炸毛框架由 Master、Manager、Worker(、TaskWorker)进程组成的。
|
||||
|
||||
如果使用 htop 工具,就比较简单,比如我启动了一个应用,使用炸毛框架编写的垃圾分类小程序 API 服务器,在 htop 命令后找到如图这部分(下面的树状图是按 F5 后切换为树状显示,避免进程刷太快可以输入 `Shift+z`):
|
||||
|
||||

|
||||
|
||||
其中,`-zsh` 下有唯一一个 php 进程,在图中对应的第一列 pid 为 `16258`,代表 Master 进程。
|
||||
|
||||
Master 进程下的唯一一个子进程(白色的是进程,绿色是线程),在图中对应的 pid 为 `16263`,代表 Manager 进程,用作管理 Worker 进程。
|
||||
|
||||
Manager 进程下的子进程,连号部分为对应的 Worker 进程,比如图中的 `16266`,`16267`,`16268`,`16269` 分别代表 `Worker #0`,`Worker #1`,`Worker #2`,`Worker #3` 四个 Worker 进程。
|
||||
|
||||
如果你还设置了 TaskWorker 进程,TaskWorker 进程的 pid 会和 Worker 进程一样是连续的,一般会接在 Worker 进程后面。
|
||||
|
||||
`htop` 使用方向键选择进程,选择到对应进程后可以使用 `F9` 来选择 kill 指令,比如让框架热重启,可以将光标移到 Master 进程上,使用 `SIGUSR1`:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
23
docs/faq/wait-message-cqbefore.md
Normal file
23
docs/faq/wait-message-cqbefore.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# CQBefore 过滤不了 waitMessage
|
||||
|
||||
因为 `waitMessage()` 功能是要等待接收下一个消息事件的,而消息事件又会被 CQBefore 走一遍。但是这里就会有一个问题,那 `waitMessage()` 的消息会不会走 CQBefore 呢?(显然不会啊!这个问题的题目就是这个!)
|
||||
|
||||
框架在 2.4.2 版本之前是无法过滤 waitMessage() 的(之前在 2.1 版本左右的几个版本是可以的,但这里不讨论历史版本),从 2.4.2 版本起支持过滤 `waitMessage`,但是需要设置一下 `@CQBefore` 的级别。
|
||||
|
||||
```php
|
||||
/**
|
||||
* @CQBefore("message",level=201)
|
||||
*/
|
||||
public function filter1() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQBefore("message")
|
||||
*/
|
||||
public function filter2() {
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
如果 `level >= 200`,那么此注解事件则会过滤 `waitMessage()`,如果 `level < 200`,则不会。(`@CQBefore` 的默认 level 为 20,所以默认情况下是不会过滤 waitMessage 的)
|
||||
@@ -4,66 +4,90 @@
|
||||
|
||||
!!! error "警告"
|
||||
|
||||
因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将将敏感信息写入配置文件并提交至开源仓库!
|
||||
因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将敏感信息写入配置文件并提交至开源仓库!
|
||||
|
||||
## 全局配置文件 global.php
|
||||
|
||||
框架的全局配置文件在 `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/` |
|
||||
| `config_dir` | 存放 saveToJson() 方法保存的数据的目录 | `zm_data` 下的 `config/` |
|
||||
| `swoole` | 对应 Swoole server 中 set 的参数,参考Swoole文档 | 见子表 `swoole` |
|
||||
| `runtime` | 一些框架运行时调整的设置 | 见子表 `runtime` |
|
||||
| `light_cache` | 轻量内置 key-value 缓存 | 见字表 `light_cache` |
|
||||
| `worker_cache` | 跨进程变量级缓存 | 见子表 `worker_cache` |
|
||||
| `mysql_config` | MySQL 数据库连接信息 | 见子表 `mysql_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 事件注解的类列表,在 Swoole 服务器启动前就被加载 | 空 |
|
||||
| `onebot` | OneBot 协议相关配置 | 见子表 `onebot` |
|
||||
| `remote_terminal` | 远程终端相关配置 | 见子表 `remote_terminal` |
|
||||
| `module_loader` | 模块/插件 加载相关配置 | 见子表 `module_loader` |
|
||||
|
||||
### 子表 **swoole**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| --------------- | ------------------------------------------------------------ | ----------------------------------- |
|
||||
| `log_file` | Swoole 的日志文件 | `crash_dir` 下的 `swoole_error.log` |
|
||||
| `worker_num` | Worker 工作进程数 | 运行框架的主机 CPU 核心数 |
|
||||
| `dispatch_mode` | 数据包分发策略,见 [文档](https://wiki.swoole.com/#/server/setting?id=dispatch_mode) | 2 |
|
||||
| `max_coroutine` | 最大协程并发数 | 300000 |
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ----------------------- | ------------------------------------------------------------ | ----------------------------------- |
|
||||
| `log_file` | Swoole 的日志文件 | `crash_dir` 下的 `swoole_error.log` |
|
||||
| `worker_num` | Worker 工作进程数 | 运行框架的主机 CPU 核心数 |
|
||||
| `dispatch_mode` | 数据包分发策略,见 [文档](https://wiki.swoole.com/#/server/setting?id=dispatch_mode) | 2 |
|
||||
| `max_coroutine` | 最大协程并发数 | 300000 |
|
||||
| `max_wait_time` | 退出进程时等待协程恢复的最长时间(秒) | 5(2.4.3 版本后默认值) |
|
||||
| `task_worker_num` | TaskWorker 工作进程数 | 默认不开启(此参数被注释) |
|
||||
| `task_enable_coroutine` | TaskWorker 工作进程启用协程 | 默认不开启(此参数被注释)或 `bool` |
|
||||
|
||||
### 子表 runtime
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ----------------------------- | ------------------------------------------------------------ | --------------------------------------- |
|
||||
| `swoole_coroutine_hook_flags` | Swoole 启动时一键协程化 Hook 的 Flag 值,详见 [一键协程化](http://wiki.swoole.com/#/runtime?id=%e5%87%bd%e6%95%b0%e5%8e%9f%e5%9e%8b) | `SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL)` |
|
||||
| `swoole_server_mode` | Swoole Server 启动的进程模式,有 `SWOOLE_PROCESS` 和 `SWOOLE_BASE` 两种,见 [启动方式](http://wiki.swoole.com/#/learn?id=swoole_process) | `SWOOLE_PROCESS` |
|
||||
| `middleware_error_policy` | 中间件错误处理策略,见 [中间件 - 错误处理策略](../../event/middleware/#_6) | 1 |
|
||||
| `reload_delay_time` | 框架 reload 重载命令接收后延迟的时间(毫秒,0 为不等待) | 800 |
|
||||
| `global_middleware_binding` | 给注解事件绑定全局中间件,见 [中间件 - 全局中间件](../../event/middleware/#_6) | `[]` |
|
||||
| `save_console_log_file` | 当这里输入字符串路径时,所有 `Console::xxx()` 输出的日志都将保存到目标文件 | false |
|
||||
|
||||
### 子表 **light_cache**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------------------------- | ----------------------------------------------- | ---------------------------- |
|
||||
| `size` | 最多可以缓存的 k-v 条目数(必须是 2 的 n 次方) | 1024 |
|
||||
| `max_strlen` | 作为 value 字符串的最大长度 | 16384 |
|
||||
| `size` | 最多可以缓存的 k-v 条目数(必须是 2 的 n 次方) | 512 |
|
||||
| `max_strlen` | 作为 value 字符串的最大长度 | 32768 |
|
||||
| `hash_conflict_proportion` | Hash冲突率(越大越好,但是需要的内存更多) | 0.6 |
|
||||
| `persistence_path` | 持久化的键值对的存储路径 | `zm_data` 下的 `_cache.json` |
|
||||
| `auto_save_interval` | 持久化的键值对自动保存时间间隔(秒) | 900 |
|
||||
|
||||
### 子表 **sql_config**
|
||||
### 子表 worker_cache
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------- | --------------------------- | ------ |
|
||||
| `worker` | 跨进程缓存的存储工作进程 id | 0 |
|
||||
|
||||
### 子表 **mysql_config**
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ------------------------ | ------------------------------ | ------------------------------------------------------------ |
|
||||
| `sql_host` | 数据库地址(留空则不使用数据库) | 空 |
|
||||
| `sql_port` | 数据库端口 | 3306 |
|
||||
| `sql_username` | 连接数据库的用户名 | |
|
||||
| `sql_database` | 要连接的数据库名 | |
|
||||
| `sql_password` | 数据库连接密码 | |
|
||||
| `sql_options` | PDO 数据库的 options 参数 | `[PDO::ATTR_STRINGIFY_FETCHES => false,PDO::ATTR_EMULATE_PREPARES => false]` |
|
||||
| `sql_default_fetch_mode` | PDO 的 fetch 模式 | `PDO::FETCH_ASSOC` |
|
||||
| `host` | 数据库地址(留空则不使用数据库) | 空 |
|
||||
| `port` | 数据库端口 | 3306 |
|
||||
| `username` | 连接数据库的用户名 | |
|
||||
| `dbname` | 要连接的数据库名 | |
|
||||
| `password` | 数据库连接密码 | |
|
||||
| `options` | PDO 数据库的 options 参数 | `[PDO::ATTR_STRINGIFY_FETCHES => false,PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]` |
|
||||
| `pool_size` | MySQL 连接池大小 | 64 |
|
||||
| `charset` | MySQL 字符集 | `utf8mb4` |
|
||||
|
||||
### 子表 **redis_config**
|
||||
|
||||
@@ -83,19 +107,39 @@
|
||||
| `document_root` | 静态文件的根目录 | `{WORKING_DIR}/resources/html` |
|
||||
| `document_index` | 默认索引的文件名列表 | `["index.html"]` |
|
||||
|
||||
### 子表 worker_cache
|
||||
### 子表 onebot
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------- | --------------------------- | ------ |
|
||||
| `worker` | 跨进程缓存的存储工作进程 id | 0 |
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| ------------------------ | ------------------------------------------------------------ | ------ |
|
||||
| `status` | 是否开启 OneBot 标准机器人解析功能 | true |
|
||||
| `single_bot_mode` | 是否开启单机器人模式 | false |
|
||||
| `message_level` | 机器人的 WebSocket 事件在 Swoole 原生事件 `@OnMessageEvent` 中的等级(越高说明越被优先处理) | 99 |
|
||||
| `message_convert_string` | 是否将数组格式的消息转换为字符串以保证与旧版本的兼容性 | true |
|
||||
| `message_command_policy` | CQCommand命令匹配后执行流程,`interrupt` 为不执行后续 CQMessage,`continue` 为继续 | `interrupt` |
|
||||
|
||||
### 子表 remote_terminal
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------- | ------------------------------------------------------------ | ----------- |
|
||||
| `status` | 是否开启远程终端功能,见 [组件 - 远程终端](/component/remote-terminal) | false |
|
||||
| `host` | 远程终端监听地址,为安全起见,默认值只允许本地回环地址(127.0.0.1) | `127.0.0.1` |
|
||||
| `port` | 远程终端监听的 TCP 端口 | 20002 |
|
||||
| `token` | 远程终端连接的令牌(如果为空("")则不验证) | "" |
|
||||
|
||||
### 子表 module_loader
|
||||
|
||||
| 配置名称 | 说明 | 默认值 |
|
||||
| -------- | ------------------------------------------------------------ | ----------- |
|
||||
| `enable_hotload` | 是否开启热加载模块包的功能 | false |
|
||||
| `load_path` | 模块包加载的目录地址 | `zm_data` 下的 `modules` |
|
||||
|
||||
## 多环境下的配置文件
|
||||
|
||||
炸毛框架的配置文件模块支持不同环境下的配置文件,主要结构为 `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
|
||||
vendor/bin/start server --env=development
|
||||
@@ -103,7 +147,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 +190,4 @@ $r = ZMConfig::get("example_a", "key1"); # $r == "value1"
|
||||
$time = ZMConfig::get("example_a", "starttime"); # $time == 服务器启动时间
|
||||
```
|
||||
|
||||
同时,自定义配置文件也支持环境变量,例如:`example_a.development.json` 或 `example_a.production.php` 均可。
|
||||
同时,自定义配置文件也支持环境变量,例如:`example_a.development.json` 或 `example_a.production.php` 均可。
|
||||
|
||||
78
docs/guide/errcode.md
Normal file
78
docs/guide/errcode.md
Normal file
@@ -0,0 +1,78 @@
|
||||
| 异常码 | 含义 | 解决方案 |
|
||||
| ------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| E00001 | 炸毛框架未检测到 PHP 安装了 Swoole 扩展 | 根据文档安装扩展去! |
|
||||
| E00002 | Swoole 扩展安装的版本过低 | 升级 Swoole 版本,最好为最新版。 |
|
||||
| E00003 | PHP 版本过低 | 升级 PHP 版本,至少为 7.2。 |
|
||||
| E00004 | Swoole 版本低于 4.6.7 且未安装 pcntl 扩展 | 安装 pcntl 扩展或升级 Swoole 至少为4.6.7。 |
|
||||
| E00005 | 在框架命令行解析过程中出现了致命错误 | 请根据提示的错误位置进行调试和修复,如果未解决请将问题反馈给作者。 |
|
||||
| E00006 | 炸毛框架在源码模式启动时未能修改 composer.json 文件 | 检查源码模式下 composer.json 文件是否正常可写可读。 |
|
||||
| E00007 | 框架在启动时未找到 global.php 全局配置文件 | 如果是使用 `composer create-project` 或用 git 来克隆 starter 仓库的,需要先使用 `vendor/bin/start init` 指令,再启动服务器。 |
|
||||
| E00008 | 框架在启动时给用于存储连接数据的共享内存表初始化失败 | 请检查系统内存是否过小,如果一切正常,此问题一般是框架内部导致的问题,请将错误日志反馈给开发者。 |
|
||||
| E00009 | 使用 `--remote-terminal` 时远程终端处理命令出现异常或致命错误 | 检查自身的远程终端是否正确配置和使用,自定义的 `@TerminalCommand` 注解是否抛出了致命错误。 |
|
||||
| E00010 | 框架在第一步的启动阶段抛出异常或致命错误,导致框架不能继续运行 | 此错误涵盖的错误内容较多,请根据实际抛出的异常内容进行处理或反馈给开发者。<br />如果你使用了 `@SwooleHandler` 或 `@OnSetup` 注解,那么可以自行检查一下注解绑定的函数有没有出错。 |
|
||||
| E00011 | 框架在调用 Swoole 服务器启动 `$server->start()` 过程中出现了异常 | 此问题未经测试,暂无解决方案,也没有遇到过,如果有发生,请将错误反馈开发者。 |
|
||||
| E00012 | 框架在启动时调用脚本解析 `@SwooleHandler` 和 `@OnSetup` 时出现了异常 | 留个坑,下次写,TODO。 |
|
||||
| E00013 | 使用命令行参数动态设置启动的 Worker/TaskWorker 进程数时输入了非法的数字 | 填写合法的数字或不使用此功能。 |
|
||||
| E00014 | 炸毛框架的启动命令报错,提示没有找到 PHP 环境 | 使用 `./install-runtime.sh` 命令安装便携的静态 PHP 环境或根据教程和 Linux 发行版安装环境。 |
|
||||
| E00015 | 启动命令启动框架找不到框架本体的入口文件 | 请检查 Composer 拉取的框架代码是否完整。 |
|
||||
| E00016 | 连接中断后 `@OnCloseEvent` 事件抛出异常 | 检查 `@OnCloseEvent` 或 `@OnSwooleEvent("close")` 注解事件。 |
|
||||
| E00017 | 框架作为 WebSocket 服务器收到客户端数据后 `@OnMessageEvent`、`@OnSwooleEvent("message")` 或 OneBot 相关事件抛出了未被捕获的异常或错误 | 检查 `@OnSwooleEvent("message")`、`@OnMessageEvent` 或 OneBot 相关注解事件。 |
|
||||
| E00018 | 框架设置 `access_token` 参数为自定义闭包函数,有新 WebSocket 连接接入但是闭包函数返回失败 | 说白了就是自定义的 `access_token` 验证失败。如果是自己的 OneBot 客户端连接,那么请检查你的函数或 OneBot 客户端那边和框架约定的 token 是否一致,如果将框架开到了公网并有人尝试连接但失败了说明是正常现象。 |
|
||||
| E00019 | 框架设置了 `access_token` 为固定字符串,但是 WebSocket 新连接验证 Token 失败 | 如果是自身行为,比如 OneBot 客户端接入,请检查 Token 是否一致。如果不需要设置 Token,请检查全局配置文件的 `access_token` 项是否为空字符串。 |
|
||||
| E00020 | 框架在收到 WebSocket 连接后触发 `@OnOpenEvent` 注解事件过程中抛出了异常 | 检查用户代码中 `@OnOpenEvent`、`@OnSwooleEvent("open")` 注解事件下的代码是否有问题。 |
|
||||
| E00021 | 框架在处理 pipeMessage 事件时出现了异常 | 如果写了 `@OnPipeMessageEvent` 注解事件,请检查对应注解事件。如果未设置,可能是框架内部错误,请将报错信息反馈开发者。 |
|
||||
| E00022 | 调用 `ProcessManager::sendActionToWorker()` 方法时,调用此方法的进程不是 Worker 或 Manager 进程 | 如果你在 Master 进程调用此方法会直接报此错误,框架不支持从 Master 进程调用此方法。 |
|
||||
| E00023 | 框架在收到 HTTP 请求后处理过程中出现了未捕获的异常 | 检查 HTTP 请求相关的注解解析代码,如果调用栈显示非用户代码所致,请将错误信息反馈开发者。 |
|
||||
| E00024 | 框架使用 `--watch` 时无法使用热更新并报错 | PHP 未安装 inotify 扩展,请使用 pecl 安装 inotify 扩展并启用后再试。 |
|
||||
| E00025 | 框架使用终端输入时产生了未捕获的异常或致命错误 | 检查 `@TerminalCommand` 注解事件或检查使用动态命令的内容(例如 bc 或 call 运行的代码或函数有没有错误)。 |
|
||||
| E00026 | 框架使用 `@OnTask` 注解在 TaskWorker 进程中执行函数抛出了异常 | 检查 TaskWorker 运行的任务代码是否会抛出未捕获的异常。 |
|
||||
| E00027 | 框架在运行过程中 Worker 进程发生未捕获的异常导致崩溃退出 | 见 [Issue #38](https://github.com/zhamao-robot/zhamao-framework/issues/38)。 |
|
||||
| E00028 | PHP 未安装 pdo_mysql(mysqlnd+PDO)扩展 | 安装 php-mysql(以 ubuntu 为例,apt install php-pdo php-mysql)。 |
|
||||
| E00029 | PHP 未安装 redis 扩展 | 安装 redis 扩展。 |
|
||||
| E00030 | 框架在 Worker 进程启动时出现错误 | 检查 `@OnStart` 相关事件的问题,或根据报错信息定位问题所在。此问题可能较常见,一般在启动时导致的。 |
|
||||
| E00031 | 框架在启动前解析代码出现错误 | 检查模块代码中是否有 PHP 语法错误。 |
|
||||
| E00032 | 上下文的 class 没有 implements ContextInterface 接口 | 如果从 global.php 设置了自定义上下文类,那么请检查上下文类有没有根据文档标准来编写接口。 |
|
||||
| E00033 | Worker 进程运行过程使用 `zm_*` 方法过程中抛出了未被捕获的异常 | 一般是由 `zm_go()` 或 `zm_timer_tik()` 造成的,协程或计时器内抛出了异常未被捕获。建议根据 trace 检查是什么地方抛出的异常。 |
|
||||
| E00034 | 由带中间件的 `@OnTick` 计时器产生了未被捕获的异常 | 建议检查计时器内的代码抛出异常位置,如果错误处理也是一部分功能,建议使用 `try catch` 自行捕获。 |
|
||||
| E00035 | CQ 码相关错误 | 根据提示检查调用 CQ 码的代码即可。 |
|
||||
| E00036 | OneBot WebSocket API 推送失败,可能是 WebSocket 客户端出现了问题 | 建议检查 OneBot 客户端和框架的连接是否正常。 |
|
||||
| E00037 | OneBot 机器人端连接未找到,或单例模式连接了多个机器人 | 根据提示信息进行修复,比如机器人 xxx 未连接到框架,就看一下 OneBot 客户端是否启用和配置正常。 |
|
||||
| E00038 | 图灵机器人 API 调用出错 | 根据提示和图灵错误码进行检查。 |
|
||||
| E00039 | 使用 build 命令时检测到目标目录不存在 | 重新指定一个存在的目录即可。 |
|
||||
| E00040 | 使用 build 命令时检测到 PHP 未设置 `phar.readonly=Off` | 修改 php.ini 将此项设置为 Off。 |
|
||||
| E00041 | 使用 init 命令时未检测到 composer.json 文件 | 检查引用框架的 composer.json 文件位置。 |
|
||||
| E00042 | 框架使用 init 命令时启动模式不是 Composer 模式 | 如果你是使用 git 且下载的仓库是 `zhamao-robot/zhamao-framework.git`,那么代表其以源码模式启动,详见[框架启动模式 - 炸毛框架 v2 (zhamao.xin)](https://framework.zhamao.xin/advanced/custom-start/)。 |
|
||||
| E00043 | MySQL 数据库出错,抛出异常 | 根据提示信息检查 MySQL 语句是否正确,数据库是否连接正常等,其他不能解决的问题建议反馈开发者。 |
|
||||
| E00044 | 打包模块过程中抛出了异常 | 根据提示文本进行修复错误的指令和代码即可。 |
|
||||
| E00045 | 打包模块过程中无法储存 `light-cache-store` 项指定的缓存数据 | 根据提示进行修复即可。 |
|
||||
| E00046 | Redis 连接池在使用过程中未提前初始化,可能是未设置全局配置文件启用 Redis 连接池 | 检查 global.php 是否设置 Redis 服务器。 |
|
||||
| E00047 | Redis 连接池初始化失败 | 根据提示报错信息进行修复,先检查 global.php 是否设置 Redis 服务器。 |
|
||||
| E00048 | LightCache 未初始化 | LightCache 会根据 global.php 初始化申请内存,如果申请出错请根据启动时的报错信息调整配置。 |
|
||||
| E00049 | LightCache 不能接收字符串、数组、int 之外的变量数据 | 检查传入的数据类型。 |
|
||||
| E00050 | 系统内存不足,LightCache 申请内存失败 | 让 PHP 可使用的内存或系统内存变大,也可以调小全局配置中设置的 LightCache 配置项。 |
|
||||
| E00051 | LightCache 的 Hash 冲突过多,导致无法动态空间分配内存 | 设置 `hash_conflict_proportion` 大一些(范围 0-1,默认是 0.6)。 |
|
||||
| E00052 | 在 /src/ 目录下不可以直接标记为模块(zm.json),因为命名空间不能为根空间 | 将模块标记文件 zm.json 放到子目录下,不能直接放在 src 目录下。 |
|
||||
| E00053 | 框架检测到了重名模块 | 更改模块名称。 |
|
||||
| E00054 | 打包好的模块文件(phar)内检测不到 zm.json 原始模块标记文件存在 | 检查 phar 模块是否完整。 |
|
||||
| E00055 | 打包好的模块文件(phar)不能正常读取模块标记文件(zm.json) | 检查 phar 模块是否完整。 |
|
||||
| E00056 | 未开启 TaskWorker 进程 | 请先修改 global 配置文件启用。 |
|
||||
| E00057 | 调用 `DataProvider::saveToJson()` 失败,因为传入了多级目录 | `saveToJson()` 方法的 `$filename` 参数只能最多到第二级子目录,不能有三级,例如 `foo/bar`。 |
|
||||
| E00058 | 调用 `DataProvider::scanDirFiles()` 失败,因为传入的 `$relative` 错误 | `$relative` 参数只能传入 `string/false` 两种类型。 |
|
||||
| E00059 | 使用 `MessageUtil::downloadCQImage()` 失败,因为指定下载的目录不存在 | 新建目录,检查目录地址是否是绝对路径,如果手动指定了目录,最好为绝对路径。 |
|
||||
| E00060 | 使用 `MessageUtil::downloadCQImage()` 失败,因为图片下载失败 | 检查下载图片的链接地址是否能正常的访问。 |
|
||||
| E00061 | 使用 `set_coroutine_params()` 失败,因为不能在非协程环境使用此函数 | 检查调用此函数的位置,注意不能在非协程环境(比如 Master 进程)下调用。 |
|
||||
| E00062 | 注解事件非法或不可回溯 | 不能在非注解调用的类中的方法调用 `EventTracer` 方法。 |
|
||||
| E00063 | 模块检测到依赖版本问题 | 检查是否部署或正确配置依赖的模块/插件版本。 |
|
||||
| E00064 | 模块系统检测到依赖的模块不存在或未安装部署 | 检查依赖的模块是否正确存在于源码目录。 |
|
||||
| E00065 | 模块系统检测到打包的模块文件中未含有 `light_cache_store.json` 文件 | 可能是打包此模块后打包的文件损坏,请询问原开发者打包一个新的没有损坏的 phar 文件。 |
|
||||
| E00066 | 模块打包时 `zmdata-store` 指定的文件或目录不存在 | 检查是否存在,检查写的相对路径是否有误(相对路径的初始路径为框架当前的 `zm_data` 配置的目录。 |
|
||||
| E00067 | 模块解包合并 `composer.json` 时没有找到项目原文件 | 检查项目的工作目录下是否有 `composer.json` 文件存在。 |
|
||||
| E00068 | 模块解包时无法正常拷贝文件 | 检查文件夹是否正常可以创建和写入。 |
|
||||
| E00069 | 框架不能启动两个 ConsoleApplication 实例 | 不要重复使用 `new ConsoleApplication()`。 |
|
||||
| E00070 | 框架找不到 `zm_data` 目录 | 检查配置中指定的 `zm_data` 目录是否存在。 |
|
||||
| E00071 | 框架找不到对应类型的 API 调用类 | 检查 `getExtendedAPI($name)` 传入的 `$name` 是否正确 |
|
||||
| E00072 | 上下文无法找到 | 检查上下文环境,如是否处于协程环境中 |
|
||||
| E00073 | 在类中找不到方法 | 检查调用对象是否存在对应的方法(method)或检查是否插入了对应的macro宏方法 |
|
||||
| E00074 | 参数非法 | 检查调用的参数是否正常(此处可能有多处问题,请看具体调用栈炸掉的地方) |
|
||||
| E99999 | 未知错误 | |
|
||||
|
||||
@@ -2,129 +2,78 @@
|
||||
|
||||
> 这篇为炸毛框架以及环境的部署教程。
|
||||
|
||||
框架部署分为环境部署和框架部署。框架部署非常简单,只需要通用的指令,下方主要说环境部署。
|
||||
框架部署分为两部分,一部分是安装 PHP 环境,另一部分是通过 Composer 或 GitHub 拉取框架的脚手架。
|
||||
|
||||
## Docker 部署 PHP 环境
|
||||
如果你不想干扰主机的环境,可以使用 Docker 进行拉取框架适用的 PHP7 with Swoole Extension Docker Container。本框架安装教程中使用的 DockerHub 及 Dockerfile 构建文件所构建的容器均为独立的容器,和框架无关,此 Docker 也可以用作运行**其他基于 php-cli 模式的项目**。
|
||||
## 一键下载静态 PHP 环境和框架脚手架
|
||||
|
||||
方法一、直接拉取远程容器(推荐)
|
||||
```bash
|
||||
docker pull zmbot/swoole
|
||||
```
|
||||
|
||||
方法二、从 Dockerfile 构建容器
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-swoole-docker.git
|
||||
cd zhamao-swoole-docker/
|
||||
docker build -t zm .
|
||||
```
|
||||
|
||||
!!! note "从 Dockerfile 构建容器的提示"
|
||||
|
||||
使用 Dockerfile 构建后,需要将下方所有的 `zmbot/swoole` 全部更换成 `zm`,或者你上方指令中的 `-t` 参数后方的名称,具体可以详情查阅 Docker 的文档。
|
||||
|
||||
## 主机部署 PHP 环境
|
||||
|
||||
### Debian 系列(Ubuntu、Kali )
|
||||
|
||||
需要的系统内软件包为:`php php-dev php-mbstring gcc make openssl php-mbstring php-json php-curl php-mysql wget composer`
|
||||
|
||||
下面是一个一键安装的命令行(最小安装,需 root 权限):
|
||||
从 2.4.4 版本起,炸毛框架支持一键拉取一个静态的 PHP 运行时和脚手架,只需运行下面的脚本即可。(开发环境推荐此方法)
|
||||
|
||||
```bash
|
||||
apt-get update && apt-get install -y software-properties-common && add-apt-repository ppa:ondrej/php && apt-get update && apt-get install php php-dev php-mbstring gcc make openssl php-mbstring php-json php-curl php-mysql -y && apt-get install wget composer -y && wget https://github.com/swoole/swoole-src/archive/v4.5.7.tar.gz && tar -zxvf v4.5.7.tar.gz && cd swoole-src-4.5.7/ && phpize && ./configure --enable-openssl --enable-mysqlnd && make -j2 && make install && (echo "extension=swoole.so" >> $(php -i | grep "Loaded Configuration File" | awk '{print $5}'))
|
||||
```
|
||||
# 将会把 PHP、框架都安装在此目录下
|
||||
mkdir zhamao-app/ # 这里可以取自己的项目名字
|
||||
cd zhamao-app/
|
||||
bash -c "$(curl -fsSL https://api.zhamao.xin/go.sh)"
|
||||
|
||||
### macOS (with Homebrew)
|
||||
|
||||
macOS 系统下的部署相对简单很多,只需要使用 Homebrew 安装以下包和执行安装命令即可
|
||||
|
||||
!!! note "给 macOS 开发者的提示"
|
||||
|
||||
因为苹果新的 Apple Sillicon 对 Homebrew 的支持目前仅限于 Rosetta2 转译版,
|
||||
所以在使用 M1-based Mac 时出现问题暂时无解。
|
||||
使用以下指令可能会遇到报错等问题,如有疑问可直接使用 Docker 或咨询我(炸毛框架开发者)。
|
||||
|
||||
```bash
|
||||
brew install php composer
|
||||
pecl install swoole
|
||||
```
|
||||
|
||||
### 其他 Linux 发行版
|
||||
|
||||
其他 Linux 发行版,如 CentOS,Fedora,Arch 等暂时还没有经过严格的测试需要哪些依赖,大体和 Ubuntu、Debian 系需要的依赖包差不多,可根据安装过程中报错提示依次安装,或者直接使用 Docker 环境。
|
||||
|
||||
## 安装框架
|
||||
|
||||
恭喜你,前方通过 Docker 或主机安装环境后可以开始构建框架的开发脚手架了!
|
||||
|
||||
如果你是通过**主机安装 PHP 部署的环境**,下方是通过脚手架来构建项目的命令行。
|
||||
|
||||
```bash
|
||||
composer create-project zhamao/framework-starter zhamao-app
|
||||
cd zhamao-app/ # 这个是你可以自己定义的名称
|
||||
vendor/bin/start server # 启动框架
|
||||
```
|
||||
|
||||
如果是通过 **Docker 部署的环境**,则需要在先克隆脚手架后在文件夹内使用 Docker 命令下的 `composer update`。(如果主机环境有 composer 也可以使用 `composer create-project` 的方式拉取脚手架。)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-framework-starter.git
|
||||
cd zhamao-framework-starter/
|
||||
docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole composer update
|
||||
```
|
||||
|
||||
或者在 Docker 环境下,你可以直接使用如下方法拉取和快速启动一个最标准的框架。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/zhamao-robot/zhamao-framework-starter.git
|
||||
cd zhamao-framework-starter
|
||||
./run-docker.sh # 在正式版炸毛框架 v2 发布后可用,测试版暂不放出
|
||||
```
|
||||
|
||||
|
||||
## 启动框架
|
||||
本地环境启动方式:
|
||||
```bash
|
||||
cd zhamao-framework-starter
|
||||
# 安装完成后的启动框架命令(2.5.0 版本后可省略掉 runtime/php 前缀)
|
||||
vendor/bin/start server
|
||||
|
||||
# 扩展用法:使用静态 PHP 版本的 Composer update
|
||||
runtime/composer update
|
||||
# 扩展用法:使用静态 PHP 运行别的 CLI 脚本
|
||||
runtime/php path/to/your/script.php
|
||||
```
|
||||
|
||||
使用 Docker 启动:
|
||||
> 有关静态 PHP 的多种用法(如 Composer),见 [进阶 - PHP 环境高级](/advanced/php-env)
|
||||
|
||||
## 使用 Docker 部署 PHP 和框架
|
||||
你也可以使用 Docker 进行拉取 PHP 环境。
|
||||
|
||||
```bash
|
||||
cd zhamao-framework-starter
|
||||
# 拉取 Docker 镜像
|
||||
docker pull zmbot/swoole
|
||||
|
||||
# 再通过 GitHub 或其他方式拉取框架脚手架
|
||||
git clone --depth=1 https://github.com/zhamao-robot/zhamao-framework-starter.git
|
||||
cd zhamao-framework-starter/
|
||||
|
||||
# Docker 内使用 Composer 更新依赖
|
||||
docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole composer update
|
||||
docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole vendor/bin/start init
|
||||
|
||||
# 使用 Docker 启动框架
|
||||
docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole vendor/bin/start server
|
||||
```
|
||||
|
||||
|
||||
启动后你会看到和下方类似的初始化内容,表明启动成功了
|
||||
|
||||
```verilog
|
||||
$ vendor/bin/start server
|
||||
host: 0.0.0.0 | port: 20001
|
||||
log_level: 2 | version: 2.0.0
|
||||
config: global.php | worker_num: 4
|
||||
working_dir: /Users/jerry/project/git-project/zhamao-framework
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
=================================================================
|
||||
working_dir: /app/zhamao-framework-starter
|
||||
listen: 0.0.0.0:20001 | worker: 4 (auto)
|
||||
environment: default | log_level: 2
|
||||
version: 2.7.0 | master_pid: 28449
|
||||
=================================================================
|
||||
______
|
||||
|__ / |__ __ _ _ __ ___ __ _ ___
|
||||
/ /| '_ \ / _` | '_ ` _ \ / _` |/ _ \
|
||||
/ /_| | | | (_| | | | | | | (_| | (_) |
|
||||
/____|_| |_|\__,_|_| |_| |_|\__,_|\___/
|
||||
|
||||
[14:27:31] [I] [#0] Worker #0 启动中
|
||||
[14:27:31] [I] [#2] Worker #2 启动中
|
||||
[14:27:31] [I] [#1] Worker #1 启动中
|
||||
[14:27:31] [I] [#3] Worker #3 启动中
|
||||
[14:27:31] [S] [#3] Worker #3 已启动
|
||||
[14:27:31] [S] [#0] Worker #0 已启动
|
||||
[14:27:31] [S] [#2] Worker #2 已启动
|
||||
[14:27:31] [S] [#1] Worker #1 已启动
|
||||
[03-20 22:30:56] [S] [#1] Worker #1 started
|
||||
[03-20 22:30:56] [S] [#2] Worker #2 started
|
||||
[03-20 22:30:56] [S] [#3] Worker #3 started
|
||||
[03-20 22:30:56] [S] [#0] Worker #0 started
|
||||
```
|
||||
|
||||
单纯运行 炸毛框架 后,如果不部署或安装启动任何机器人客户端的话,仅仅相当于启动了一个 监听 20001 端口的WebSoket + HTTP 服务器。你可以通过浏览器访问:http://127.0.0.1:20001 ,或者你部署到了服务器后需要输入服务器地址。
|
||||
|
||||
!!! note "安装和部署总结"
|
||||
## 命令总结
|
||||
|
||||
根据上方描述,此文档中剩余提到的所有 Bash 命令,如果使用 Docker 部署环境,则需要加上 Docker 环境的指令:`docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole`,如执行其他 Linux 指令(以查看 PHP 版本为例):`docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole php -v`。
|
||||
1. 对于框架的启动,必须 cd 到项目的跟目录,比如 `cd zhamao-app/` 进入到项目根目录。
|
||||
2. 无论何种方式启动,启动框架的命令格式都为这个格式:`{php二进制路径} vendor/bin/start server {--如果需要参数的话这样跟}`
|
||||
3. 第二条的 `php 二进制路径` 指的是,比如使用第一种静态 PHP 环境,这里写 `runtime/php` 就好了,如果是安装到系统的 PHP 的话,这里为空,如果是 Docker 部署的环境,则这里填 `docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole`
|
||||
|
||||
## 使用 IDE 等工具开发代码
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
一切都安装成功后,你就已经做好了进行简单配置以运行一个最小的 **机器人问答模块** 的准备。
|
||||
|
||||
炸毛框架和机器人客户端是什么关系呢?炸毛框架就好比我们传统的一系列例如 Spring 框架、ThinkPHP 框架等,是服务端,而机器人客户端是一个 HTTP / WebSocket 客户端,时刻准备着连接到炸毛框架的。
|
||||
炸毛框架和机器人客户端是什么关系呢?炸毛框架就好比我们传统的一系列例如 Spring 框架、ThinkPHP 框架等,是服务端,而机器人客户端是一个 HTTP / WebSocket 客户端,时刻准备着连接到炸毛框架。
|
||||
|
||||
## 机器人客户端
|
||||
|
||||
@@ -30,52 +30,103 @@ OneBot 机器人部分的选择详情见 [OneBot 实例](/guide/OneBot实例/)
|
||||
|
||||
由于 go-cqhttp 项目还处于开发期,而且配置文件格式也发生了多次变化,但大体内容没有变(比如编写此文档时发布的版本中配置文件格式变成了 `hjson` 取代了原来的 `json`。
|
||||
|
||||
=== "config.json(旧格式)"
|
||||
=== "config.yml(最新格式)"
|
||||
|
||||
``` 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": ""
|
||||
}
|
||||
```yaml hl_lines="2 3 78 83"
|
||||
account: # 账号相关
|
||||
uin: 1233456 # QQ账号
|
||||
password: '' # 密码为空时使用扫码登录
|
||||
encrypt: false # 是否开启密码加密
|
||||
status: 0 # 在线状态 请参考 https://github.com/Mrs4s/go-cqhttp/blob/dev/docs/config.md#在线状态
|
||||
relogin: # 重连设置
|
||||
delay: 3 # 首次重连延迟, 单位秒
|
||||
interval: 3 # 重连间隔
|
||||
max-times: 0 # 最大重连次数, 0为无限制
|
||||
|
||||
# 是否使用服务器下发的新地址进行重连
|
||||
# 注意, 此设置可能导致在海外服务器上连接情况更差
|
||||
use-sso-address: true
|
||||
|
||||
heartbeat:
|
||||
# 心跳频率, 单位秒
|
||||
# -1 为关闭心跳
|
||||
interval: 5
|
||||
|
||||
message:
|
||||
# 上报数据类型
|
||||
# 可选: string,array
|
||||
post-format: string
|
||||
# 是否忽略无效的CQ码, 如果为假将原样发送
|
||||
ignore-invalid-cqcode: false
|
||||
# 是否强制分片发送消息
|
||||
# 分片发送将会带来更快的速度
|
||||
# 但是兼容性会有些问题
|
||||
force-fragment: false
|
||||
# 是否将url分片发送
|
||||
fix-url: false
|
||||
# 下载图片等请求网络代理
|
||||
proxy-rewrite: ''
|
||||
# 是否上报自身消息
|
||||
report-self-message: false
|
||||
# 移除服务端的Reply附带的At
|
||||
remove-reply-at: false
|
||||
# 为Reply附加更多信息
|
||||
extra-reply-data: false
|
||||
|
||||
output:
|
||||
# 日志等级 trace,debug,info,warn,error
|
||||
log-level: warn
|
||||
# 是否启用 DEBUG
|
||||
debug: false # 开启调试模式
|
||||
|
||||
# 默认中间件锚点
|
||||
default-middlewares: &default
|
||||
# 访问密钥, 强烈推荐在公网的服务器设置
|
||||
access-token: ''
|
||||
# 事件过滤器文件目录
|
||||
filter: ''
|
||||
# API限速设置
|
||||
# 该设置为全局生效
|
||||
# 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配
|
||||
# 目前该限速设置为令牌桶算法, 请参考:
|
||||
# https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin
|
||||
rate-limit:
|
||||
enabled: false # 是否启用限速
|
||||
frequency: 1 # 令牌回复频率, 单位秒
|
||||
bucket: 1 # 令牌桶大小
|
||||
|
||||
database: # 数据库相关设置
|
||||
leveldb:
|
||||
# 是否启用内置leveldb数据库
|
||||
# 启用将会增加10-20MB的内存占用和一定的磁盘空间
|
||||
# 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能
|
||||
enable: true
|
||||
|
||||
# 连接服务列表
|
||||
servers:
|
||||
# 添加方式,同一连接方式可添加多个,具体配置说明请查看文档
|
||||
#- http: # http 通信
|
||||
#- ws: # 正向 Websocket
|
||||
#- ws-reverse: # 反向 Websocket
|
||||
#- pprof: #性能分析服务器
|
||||
# Zhamao Framework 所需要的服务器配置
|
||||
- ws-reverse:
|
||||
# 是否禁用当前反向WS服务
|
||||
disabled: false
|
||||
# 反向WS Universal 地址
|
||||
# 注意 设置了此项地址后下面两项将会被忽略
|
||||
universal: ws://127.0.0.1:20001/
|
||||
# 反向WS API 地址
|
||||
api: ws://your_websocket_api.server
|
||||
# 反向WS Event 地址
|
||||
event: ws://your_websocket_event.server
|
||||
# 重连间隔 单位毫秒
|
||||
reconnect-interval: 3000
|
||||
middlewares:
|
||||
<<: *default # 引用默认中间件
|
||||
```
|
||||
|
||||
=== "config.hjson(新格式)"
|
||||
=== "config.hjson(v1.0.0-beta2或更早版本所用格式)"
|
||||
|
||||
``` json hl_lines="3 5 81 84"
|
||||
{
|
||||
@@ -193,6 +244,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
|
||||
|
||||
## 第一次对话
|
||||
@@ -224,5 +320,21 @@ public function repeat() {
|
||||
|
||||
这样,一个简易的复读机就做好了!回到 QQ 机器人聊天,向机器人发送 `echo 你好啊`,它会回复你 `你好啊`。
|
||||
|
||||
<chat-box>
|
||||
) echo 你好啊
|
||||
( 你好啊
|
||||
) echo
|
||||
( 请输入你要回复的内容
|
||||
) 哦豁
|
||||
( 哦豁
|
||||
</chat-box>
|
||||
|
||||
> 如果你只回复 `echo` 的话,它会先和你进入一个会话状态,并问你 `请输入你要回复的内容`,这时你再次说一些内容例如 `哦豁`,会回复你 `哦豁`。效果和直接输入 `echo 哦豁` 是一致的,这是炸毛框架内的一个封装好的命令参数对话询问功能。有关参数询问功能,请看后面的进阶模块。
|
||||
|
||||
|
||||
|
||||
## 使用机器人 API 和事件
|
||||
|
||||
如果你想不只是回复消息,还要做其他复杂的动作(Action),使用 OneBot Action(又名 OneBot API)进行发送即可,见 [机器人 API](/component/bot/robot-api)。
|
||||
|
||||
如果想处理其他类型的事件,比如 QQ 群通知事件等,见 [机器人注解事件](/event/robot-annotations/)。
|
||||
37
docs/guide/upgrade.md
Normal file
37
docs/guide/upgrade.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 升级指南
|
||||
|
||||
因为框架在随着需求以及 Bug 在不断更新,所以在未来框架会发布新版本。为了方便从旧版本安装并使用框架的开发者无损更新到新版本,这里提供了升级版本上需要注意的事项。
|
||||
|
||||
## 版本约定
|
||||
|
||||
炸毛框架的版本号一般情况均按照 [Semantic Versioning 2.0.0](https://semver.org/) 标准进行滚动发行,规则简述如下:
|
||||
|
||||
假设版本号为 x.y.z:
|
||||
|
||||
- `x` 为大版本,一般只有在发生完全无法兼容的更新时增加,需要开发者最重视。
|
||||
- `y` 为小发行版本,默认情况下会新增组件功能,但会尽可能兼容旧版本,存在不兼容情况极少。
|
||||
- `z` 为补丁版本,在不进行任何大功能更新情况下提供 Bug 的修复,完全兼容前版本。
|
||||
|
||||
例如,炸毛框架的 `2.4.2` 版本,在 `2.5.0` 发行后,框架提供了大量新组件,但是对旧版本的配置和组件完全兼容,无任何额外的说明,则可以直接升级。
|
||||
|
||||
## 升级方法
|
||||
|
||||
根据安装方法不同,升级的方法也不同。
|
||||
|
||||
框架安装方式有多种,但主要分为三类:
|
||||
|
||||
- Composer 加载库的方式
|
||||
- 框架源码模式
|
||||
- Phar 打包模式
|
||||
|
||||
在 Composer 加载库的方式下,一般是指使用命令 `composer require zhamao/framework` 或 `composer create-project zhamao/framework-starter` 的方式安装框架,框架的核心文件都在 `vendor` 目录下。
|
||||
|
||||
此方式安装的框架升级最方便,直接执行命令 `composer update` 即可。
|
||||
|
||||
框架源码模式安装一般为直接使用 `git clone` 框架本体的 GitHub 仓库或下载 master 分支安装,这种情况不可升级版本(或使用 `git pull` 拉取)。
|
||||
|
||||
Phar 打包模式更新则必须重新自行打包新版本,例如从 Composer 加载库方式打包的框架,则需在原目录使用 `composer update` 后再次打包一个新版本。
|
||||
|
||||
## 升级提示
|
||||
|
||||
如果在升级过程中遇到了提示,则可能是需要升级某些配置文件需要手动进行合并更新。如果提示了更新,建议到 `vendor/zhamao/framework/config/global.php` 框架的最新库内配置文件与 `config/global.php` 文件进行对比。
|
||||
@@ -1,16 +1,14 @@
|
||||
# 介绍
|
||||
|
||||
> 本文档为炸毛框架 v2 版本,如需查看 v1 版本,[点我](https://docs-v1.zhamao.xin/)。
|
||||
!!! tip "提示"
|
||||
|
||||
> 如果是从 v1.x 版本升级到 v2.x,[点我看升级指南](/advanced/to-v2/)。
|
||||
编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request,以此来贡献此文档!
|
||||
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(CQHTTP 对接),包含 websocket、http 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(OneBot 标准的机器人对接),包含 WebSocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。
|
||||
|
||||
框架主要用途为 HTTP 服务器,机器人搭建框架。尤其对于 QQ 机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
|
||||
框架主要用途为 HTTP/WebSocket 服务器,机器人搭建框架。尤其对于聊天机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。
|
||||
|
||||
在 HTTP 和 WebSocket 服务器上,PHP 的扩展 Swoole 提供了高性能的支持,使其效率可媲美 nginx 静态网页处理的效率。
|
||||
|
||||
此外,QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快,未来也会兼容微信公众号开发者模式。
|
||||
此外,QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快。
|
||||
|
||||
```php
|
||||
/**
|
||||
@@ -34,18 +32,18 @@ public function index() {
|
||||
首先,你需要了解你需要知道哪些事情才能开始着手使用框架:
|
||||
|
||||
1. Linux 命令行(会跑 Linux 程序)
|
||||
2. php 7.2+ 开发环境
|
||||
3. HTTP 协议(可选)
|
||||
4. OneBot 机器人聊天接口标准(可选)
|
||||
2. php >=7.2 开发环境(项目会持续支持最新的 PHP 版本)
|
||||
4. OneBot 机器人聊天接口标准
|
||||
|
||||
需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。
|
||||
|
||||
!!! bug "文档提示"
|
||||
|
||||
此文档采用 MkDocs 驱动,但因为本文档的搜索组件原生不支持中文搜索,所以搜索体验会大打折扣,敬请谅解!搜不到不是没这个东西哦!
|
||||
此文档采用 MkDocs 驱动,文档的搜索组件原生不支持中文搜索,且分词很难控制,所以搜索体验会大打折扣,敬请谅解!搜不到不是没这个东西,建议这种情况可以自行翻阅目录查看!
|
||||
|
||||
|
||||
## 框架特色
|
||||
|
||||
- 支持MySQL数据库(连接池),自带查询缓存提高多查询时的效率
|
||||
- Websocket 服务器、HTTP 服务器兼容运行,一个框架多个用处
|
||||
- 支持命令、自然语言处理等多种插件形式
|
||||
@@ -57,6 +55,7 @@ public function index() {
|
||||
## 文档主题
|
||||
|
||||
### 主题
|
||||
|
||||
<div class="tx-switch">
|
||||
<button data-md-color-scheme="default"><code>默认模式</code></button>
|
||||
<button data-md-color-scheme="slate"><code>暗黑模式</code></button>
|
||||
@@ -76,6 +75,7 @@ public function index() {
|
||||
</script>
|
||||
|
||||
### 主色调
|
||||
|
||||
<div class="tx-switch">
|
||||
<button data-md-color-primary="red"><code>red</code></button>
|
||||
<button data-md-color-primary="pink"><code>pink</code></button>
|
||||
@@ -101,6 +101,7 @@ public function index() {
|
||||
</div>
|
||||
|
||||
### 辅色调
|
||||
|
||||
<div class="tx-switch"> <button data-md-color-accent="red"><code>red</code></button> <button data-md-color-accent="pink"><code>pink</code></button> <button data-md-color-accent="purple"><code>purple</code></button> <button data-md-color-accent="deep-purple"><code>deep purple</code></button> <button data-md-color-accent="indigo"><code>indigo</code></button> <button data-md-color-accent="blue"><code>blue</code></button> <button data-md-color-accent="light-blue"><code>light blue</code></button> <button data-md-color-accent="cyan"><code>cyan</code></button> <button data-md-color-accent="teal"><code>teal</code></button> <button data-md-color-accent="green"><code>green</code></button> <button data-md-color-accent="light-green"><code>light green</code></button> <button data-md-color-accent="lime"><code>lime</code></button> <button data-md-color-accent="yellow"><code>yellow</code></button> <button data-md-color-accent="amber"><code>amber</code></button> <button data-md-color-accent="orange"><code>orange</code></button> <button data-md-color-accent="deep-orange"><code>deep orange</code></button> </div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -42,7 +42,7 @@ function setCookie(name, value) {
|
||||
var Days = 30;
|
||||
var exp = new Date();
|
||||
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
|
||||
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
|
||||
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/";
|
||||
}
|
||||
|
||||
s_theme=getCookie("_theme");
|
||||
|
||||
44
docs/javascripts/library/highlight.min.js
vendored
Normal file
44
docs/javascripts/library/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
194
docs/update/build-update.md
Normal file
194
docs/update/build-update.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# 更新日志(master 分支 commit)
|
||||
|
||||
此文档将显示非发布版的提交版本相关更新文档,可能与发布版的更新日志有重合,在此仅作更新记录。
|
||||
|
||||
同时此处将只使用 build 版本号进行区分。
|
||||
|
||||
## build 448 (2022-3-20)
|
||||
|
||||
- 加快 build 命令的执行速度,取消进度条和提升性能
|
||||
|
||||
## build 447 (2022-3-20)
|
||||
|
||||
- 发布 2.7.0 正式版
|
||||
|
||||
## build 446 (2022-3-20)
|
||||
|
||||
- 新增 `./zhamao server` 下的 `--no-state-check` 参数,关闭“启动框架前的运行状态检查”功能
|
||||
|
||||
## build 445 (2022-3-20)
|
||||
|
||||
- 新增配置项 `runtime`.`annotation_reader_ignore`:支持注解解析器忽略注解的自定义
|
||||
|
||||
## build 444 (2022-3-20)
|
||||
|
||||
- 更改 `extra`.`exclude_annotate` 为 `zm`.`exclude-annotation-path` 项
|
||||
|
||||
## build 443 (2022-3-20)
|
||||
|
||||
- 修复注释空格的样式
|
||||
|
||||
## build 442 (2022-3-20)
|
||||
|
||||
- 修复打包模块后 `files` 的 autoload 项不能被解压和引入的 Bug
|
||||
|
||||
## build 441 (2022-3-20)
|
||||
|
||||
- 修复打包模块时命名空间与实际不一致的 Bug
|
||||
|
||||
## build 440 (2022-3-20)
|
||||
|
||||
- 新增方法宏(Macroable)
|
||||
|
||||
## build 439 (2022-3-19)
|
||||
|
||||
- 新增 PHP 8 Attribute 与注解同时支持的特性
|
||||
|
||||
## build 438 (2022-3-18)
|
||||
|
||||
- 修复 Response 类在 PHP 8.1 环境下的报错
|
||||
|
||||
## build 437 (2022-3-17)
|
||||
|
||||
- 修复 `ctx()` 可能会返回 null 的 Bug
|
||||
|
||||
## build 436 (2022-3-15)
|
||||
|
||||
- 新增 PHPStan 和 PHP CS Fixer 并优化全局代码
|
||||
|
||||
## build 435 (2022-3-13)
|
||||
|
||||
- 优化分离 WorkerManager 与 ProcessManager 的职责
|
||||
- 新增 Ctrl+C 一次无法停止框架时多次 Ctrl+C 后可强行杀掉所有进程的功能
|
||||
- `./zhamao server:stop` 新增参数 `--force`,使用 `SIGKILL` 强行杀掉所有进程
|
||||
- 新增 AnnotationParser 对 `autoload-dev` 项中的 `psr-4` 默认检索条件
|
||||
- 新增框架启动状态检测功能,如果已经启动了同样目录的框架,则会报错
|
||||
- 新增“强制启用轮询模式启动热更新”功能(参数 `--polling-watch`)
|
||||
- 修复与 PHP 8.1 的兼容性
|
||||
- 对 DaemonCommand 进行优化,与 ServerCommand 效果相同
|
||||
- 修复 `autoload`.`psr-4` 不存在时报错的 Bug
|
||||
- 新增框架停止时 Worker 退出回显状态码
|
||||
- 新增 inotify 判断模式,如果使用 `--watch` 检测到没有安装 inotify,则自动使用轮询模式
|
||||
|
||||
## build 434 (2022-1-8)
|
||||
|
||||
- 修复框架在 PHP 8.1 下运行时的一些问题
|
||||
- 新增 Console 日志输出到文件的功能
|
||||
|
||||
## build 433 (2021-12-28)
|
||||
|
||||
- 修复 OneBotV11 因 IDE 自动优化导致 API 接口发生变化的问题
|
||||
|
||||
## build 432 (2021-12-25)
|
||||
|
||||
- 新增 GoCqhttpAPI 包,用于支持额外的 OneBot Action(API)
|
||||
- 修复 MySQL 查询器中 `fetchOne()` 方法无法正确返回值的 Bug
|
||||
- 修复 Swoole Hook 因配置不当无法正确使用的 Bug
|
||||
|
||||
## build 431 (2021-12-22)
|
||||
|
||||
- 修复 Issue #50
|
||||
- 新增 PhpStorm IDE 直接运行框架的脚本
|
||||
|
||||
## build 430 (2021-12-8)
|
||||
|
||||
- 删除调试信息
|
||||
- 修复 #56 中关于数据库组件的 Bug
|
||||
|
||||
## build 429 (2021-12-7)
|
||||
|
||||
- 新增配置项 `onebot`.`message_command_policy`
|
||||
- 新增 CQCommand 阻断策略的自定义配置功能
|
||||
- 修复 CQAfter 无法正常使用的 bug #53
|
||||
|
||||
## build 428 (2021-11-16)
|
||||
|
||||
- 修复 `ctx()->waitMessage()` 在 array 消息模式下无法正确返回消息字符串的问题
|
||||
- 新增 `ctx()->getArrayMessage()` 和 `ctx()->getStringMessage()` 两个方法
|
||||
- 修复注解事件 `CQCommand` 和 `CQMessage` 在 array 消息模式下无法正确解析的 Bug
|
||||
|
||||
## build 427 (2021-11-16)
|
||||
|
||||
- 新增全局中间件,可在全局配置文件中设置
|
||||
- 修复部分 Typo
|
||||
- 新增指令 `server:status`、`server:reload`、`server:stop` 可用在新开终端中查看框架运行状态、重启和退出
|
||||
- 新增支持 `array` 格式的消息
|
||||
- 上下文 Context 对象新增 `getOriginMessage()` 用于获取原消息,`getMessage()` 如果在设置了转换后,将默认转换消息为字符串格式保持与旧模块兼容
|
||||
- OneBot API 新增全局过滤器,可用作 Action 过滤重写等操作
|
||||
- 配置文件新增 `runtime.reload_delay_time`,用于可配置重载 Worker 等待的时间(毫秒)
|
||||
- 配置文件新增 `runtime.global_middleware_binding`,用于配置全局中间件
|
||||
- 配置文件新增 `onebot.message_convert_string`,用于配置是否转换数组格式为字符串,保证与前版本的兼容性(默认为 true)
|
||||
- MessageUtil 消息工具类新增方法:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)`
|
||||
- MessageUtil 消息工具类新增方法:`arrayToStr(array $array)`
|
||||
- 新增框架启动多次监测功能,无法使用同一个框架项目同时启动两个框架
|
||||
|
||||
## build 426 (2021-11-10)
|
||||
|
||||
- 修复 CQ 码的解析函数 Bug(#52)
|
||||
|
||||
## build 425 (2021-11-3)
|
||||
|
||||
- 删除未实际应用功能的配置参数
|
||||
- 修复 reload 时会断开 WebSocket 连接且导致进程崩溃的 Bug
|
||||
|
||||
## build 424 (2021-11-2)
|
||||
|
||||
- 新增 InstantModule 类、ZMServer 类、ModuleBase 类
|
||||
- 配置文件新增 `runtime.reload_kill_connect`、`runtime.global_middleware_binding` 选项
|
||||
- 修复部分情况下闭包事件分发时崩溃的 bug
|
||||
- 新增内部方法 `_zm_env_check`
|
||||
- 调整默认的 OneBot 模块对应的等级从 99999 调整为 99
|
||||
- 新增导出框架运行参数的列表功能
|
||||
|
||||
## build 423 (2021-10-17)
|
||||
|
||||
- 修复 PHP 7.2 ~ 7.3 下无法使用新版 MySQL 组件的 bug
|
||||
|
||||
## build 422 (2021-10-6)
|
||||
|
||||
- 修复 `script_` 前缀无法被排除加载模块的 bug
|
||||
- 修复 MySQL 组件的依赖问题
|
||||
|
||||
## build 421 (2021-9-11)
|
||||
|
||||
- 删除多余的调试信息
|
||||
|
||||
## build 420 (2021-9-11)
|
||||
|
||||
- 修复 OneBot 事件无法响应的 bug
|
||||
- 新增部分 EventDispatcher 触发的事件 debug 日志
|
||||
|
||||
## build 419 (2021-9-11)
|
||||
|
||||
- 修复 DB 模块在未连接数据库的时候抛出未知异常
|
||||
- 修复部分情况下打包模块出现的错误
|
||||
|
||||
## build 418 (2021-9-10)
|
||||
|
||||
- 修复 ZMAtomic 在 test 环境下的 bug
|
||||
- 修复 MessageUtil 的报错
|
||||
|
||||
## build 417 (2021-8-29)
|
||||
|
||||
- 新增 AnnotationException,统一框架内部的抛出异常的类型
|
||||
- 新增 AnnotationParser 下的 `verifyMiddlewares()` 方法
|
||||
- 私有化 CQAPI 类下的内部方法
|
||||
- 将 WebSocket API 响应超时时间从 60 秒缩短为 30 秒
|
||||
- 修复 DB 类不能使用旧查询器的 bug
|
||||
- 统一 DB 类下抛出 Exception 的类型为 ZMException 的子类
|
||||
- EventDispatcher 新增对 `middleware_error_policy` 的处理段
|
||||
- 配置文件下 `runtime` 新增 `middleware_error_policy` 字段
|
||||
- 将 LightCache 组件抛出的异常改为 LightCacheException
|
||||
- ModuleManager 修复改配置的 `load_path` 不生效的 bug
|
||||
- 修复打包时生成的 Phar Autoload 列表出错的 bug
|
||||
- 将配置的 override 改为 overwrite
|
||||
- 新增解包时忽略依赖的选项(`--ignore-depends`)
|
||||
- 删除众多调试日志,修改部分调试日志为 debug 级别的输出
|
||||
- 修改 `ZM\MySQL\MySQLManager` 下的 `getConnection()` 为 `getWrapper()`
|
||||
- MySQLPool 对象新增 `getCount()` 方法
|
||||
- 新增 MySQLQueryBuilder 类(`doctrine/dbal` 的 wrapper 类)
|
||||
- 修复 MySQLStatement 封装原 dbal 组件时与连接池不兼容的 bug
|
||||
- 新增 MySQLStatementWrapper 类
|
||||
- 完善 MySQLWrapper 类,用作主要的查询对象控制类
|
||||
- 编写外部插件加载方式(Phar 热加载功能)
|
||||
- 修复 `ZMUtil::getClassesPsr4()` 方法在遇到空扩展名文件时的报错
|
||||
78
docs/update/config.md
Normal file
78
docs/update/config.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# 配置文件变更记录
|
||||
|
||||
这里将会记录各个主版本的框架升级后,涉及 `global.php` 的更新日志,你可以根据这里描述的内容与你的旧配置文件进行合并。
|
||||
|
||||
## v2.7.0 (build 447)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `annotation_reader_ignore` 项。
|
||||
|
||||
## v2.6.6 (build 434)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `save_console_log_file` 项。
|
||||
|
||||
## v2.6.0 (build 427)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `reload_delay_time`、`global_middleware_binding` 项。
|
||||
- 新增 `$config['onebot']` 下的 `message_convert_string` 项。
|
||||
|
||||
## v2.5.1 (build 417)
|
||||
|
||||
- 新增 `$config['runtime']` 下的 `middleware_error_policy` 选项。
|
||||
|
||||
## v2.5.0 (build 413)
|
||||
|
||||
- 新增 `$config['runtime']` 运行时设置。
|
||||
- 删除 `$config['server_event_handler_class']`,默认在启动时全局扫描。
|
||||
- 新增 `$config['module_loader']` 模块/插件 打包配置选项。
|
||||
- 新增 `$config['mysql_config']`,取代原先的 `$config['sql_config']`,此外废弃原先的MySQL 查询器 `\ZM\DB\DB` 类。
|
||||
|
||||
更新部分:
|
||||
|
||||
```php
|
||||
/** 一些框架与Swoole运行时设置的调整 */
|
||||
$config['runtime'] = [
|
||||
'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL),
|
||||
'swoole_server_mode' => SWOOLE_PROCESS
|
||||
];
|
||||
|
||||
/** MySQL数据库连接信息,host留空则启动时不创建sql连接池 */
|
||||
$config['mysql_config'] = [
|
||||
'host' => '',
|
||||
'port' => 3306,
|
||||
'unix_socket' => null,
|
||||
'username' => 'root',
|
||||
'password' => '123456',
|
||||
'dbname' => 'adb',
|
||||
'charset' => 'utf8mb4',
|
||||
'pool_size' => 64,
|
||||
'options' => [
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
|
||||
]
|
||||
];
|
||||
|
||||
/** 注册 Swoole Server 事件注解的类列表(deleted) */
|
||||
// 删除
|
||||
```
|
||||
|
||||
## 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,5 +1,439 @@
|
||||
# 更新日志(v2 版本)
|
||||
|
||||
## v2.7.1(build 448)
|
||||
|
||||
> 更新时间:2022.3.20
|
||||
|
||||
- 加快 build 命令的执行速度,取消进度条和提升性能
|
||||
|
||||
## v2.7.0(build 447)
|
||||
|
||||
> 更新时间:2022.3.20
|
||||
|
||||
- 优化分离 WorkerManager 与 ProcessManager 的职责
|
||||
- 新增 Ctrl+C 一次无法停止框架时多次 Ctrl+C 后可强行杀掉所有进程的功能
|
||||
- `./zhamao server:stop` 新增参数 `--force`,使用 `SIGKILL` 强行杀掉所有进程
|
||||
- 新增 AnnotationParser 对 `autoload-dev` 项中的 `psr-4` 默认检索条件
|
||||
- 新增框架启动状态检测功能,如果已经启动了同样目录的框架,则会报错
|
||||
- 新增“强制启用轮询模式启动热更新”功能(参数 `--polling-watch`)
|
||||
- 修复与 PHP 8.1 的兼容性
|
||||
- 对 DaemonCommand 进行优化,与 ServerCommand 效果相同
|
||||
- 修复 `autoload`.`psr-4` 不存在时报错的 Bug
|
||||
- 新增框架停止时 Worker 退出回显状态码
|
||||
- 新增 inotify 判断模式,如果使用 `--watch` 检测到没有安装 inotify,则自动使用轮询模式
|
||||
- 新增 PHPStan 和 PHP CS Fixer 并优化全局代码
|
||||
- 修复 `ctx()` 可能会返回 null 的 Bug
|
||||
- 修复 Response 类在 PHP 8.1 环境下的报错
|
||||
- 新增 PHP 8 Attribute 与注解同时支持的特性
|
||||
- 新增方法宏(Macroable)
|
||||
- 修复打包模块时命名空间与实际不一致的 Bug
|
||||
- 修复打包模块后 `files` 的 autoload 项不能被解压和引入的 Bug
|
||||
- 修复注释空格的样式
|
||||
- 更改 `extra`.`exclude_annotate` 为 `zm`.`exclude-annotation-path` 项
|
||||
- 新增配置项 `runtime`.`annotation_reader_ignore`:支持注解解析器忽略注解的自定义
|
||||
- 新增 `./zhamao server` 下的 `--no-state-check` 参数,关闭“启动框架前的运行状态检查”功能
|
||||
|
||||
## v2.6.6(build 434)
|
||||
|
||||
> 更新时间:2022.1.8
|
||||
|
||||
- 修复框架在 PHP 8.1 下运行时的一些问题
|
||||
- 新增 Console 日志输出到文件的功能
|
||||
|
||||
## v2.6.5(build 433)
|
||||
|
||||
> 更新时间:2021.12.28
|
||||
|
||||
- 修复 OneBotV11 因 IDE 自动优化导致 API 接口发生变化的问题
|
||||
|
||||
## v2.6.4(build 432)
|
||||
|
||||
> 更新时间:2021.12.25
|
||||
|
||||
- 新增 GoCqhttpAPI 包,用于支持额外的 OneBot Action(API)
|
||||
- 修复 MySQL 查询器中 `fetchOne()` 方法无法正确返回值的 Bug
|
||||
- 修复 Swoole Hook 因配置不当无法正确使用的 Bug
|
||||
- 修复 Issue #50
|
||||
- 新增 PhpStorm IDE 直接运行框架的脚本
|
||||
|
||||
## v2.6.3 (build 430)
|
||||
|
||||
> 更新时间:2021.12.8
|
||||
|
||||
- 删除调试信息
|
||||
- 修复 #56 中关于数据库组件的 Bug
|
||||
|
||||
## v2.6.2 (build 429)
|
||||
|
||||
> 更新时间:2021.12.7
|
||||
|
||||
- 新增配置项 `onebot`.`message_command_policy`
|
||||
- 新增 CQCommand 阻断策略的自定义配置功能
|
||||
- 修复 CQAfter 无法正常使用的 bug #53
|
||||
|
||||
## v2.6.1 (build 428)
|
||||
|
||||
> 更新时间:2021.11.16
|
||||
|
||||
- 修复 ctx()->waitMessage() 在 array 消息模式下无法正确返回消息字符串的问题
|
||||
- 新增 ctx()->getArrayMessage() 和 ctx()->getStringMessage() 两个方法
|
||||
- 修复注解事件 CQCommand 和 CQMessage 在 array 消息模式下无法正确解析的 Bug
|
||||
|
||||
## v2.6.0 (build 427)
|
||||
|
||||
> 更新时间:2021.11.16
|
||||
|
||||
- 新增全局中间件,可在全局配置文件中设置
|
||||
- 修复部分 Typo
|
||||
- 新增指令 `server:status`、`server:reload`、`server:stop` 可用在新开终端中查看框架运行状态、重启和退出
|
||||
- 新增支持 `array` 格式的消息
|
||||
- 上下文 Context 对象新增 `getOriginMessage()` 用于获取原消息,`getMessage()` 如果在设置了转换后,将默认转换消息为字符串格式保持与旧模块兼容
|
||||
- OneBot API 新增全局过滤器,可用作 Action 过滤重写等操作
|
||||
- 配置文件新增 `runtime.reload_delay_time`,用于可配置重载 Worker 等待的时间(毫秒)
|
||||
- 配置文件新增 `runtime.global_middleware_binding`,用于配置全局中间件
|
||||
- 配置文件新增 `onebot.message_convert_string`,用于配置是否转换数组格式为字符串,保证与前版本的兼容性(默认为 true)
|
||||
- MessageUtil 消息工具类新增方法:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)`
|
||||
- MessageUtil 消息工具类新增方法:`arrayToStr(array $array)`
|
||||
- 新增框架启动多次监测功能,无法使用同一个框架项目同时启动两个框架
|
||||
|
||||
## v2.5.8 (build 426)
|
||||
|
||||
> 更新时间:2021.11.10
|
||||
|
||||
- 修复 CQ 码的解析函数 Bug(#52)
|
||||
|
||||
## v2.5.7 (build 425)
|
||||
|
||||
> 更新时间:2021.11.3
|
||||
|
||||
- 调低 OneBot 相关事件在 Swoole 的优先级
|
||||
- 修复部分情况下闭包事件函数分发时引发的崩溃 bug
|
||||
- 修复 reload 时会断开 WebSocket 连接且导致进程崩溃的 bug
|
||||
|
||||
## v2.5.6 (build 423)
|
||||
|
||||
> 更新时间:2021.10.17
|
||||
|
||||
- 修复 PHP 7.2 ~ 7.3 下无法使用新版 MySQL 组件的 bug
|
||||
|
||||
## v2.5.5 (build 422)
|
||||
|
||||
> 更新时间:2021.10.6
|
||||
|
||||
- 修复 `script_` 前缀无法被排除加载模块的 bug
|
||||
- 修复 MySQL 组件的依赖问题
|
||||
|
||||
## v2.5.4 (buidl 421)
|
||||
|
||||
> 更新时间:2021.9.11
|
||||
|
||||
- 删除多余的调试信息
|
||||
|
||||
## v2.5.3 (build 420)
|
||||
|
||||
> 更新时间:2021.9.11
|
||||
|
||||
- 修复 DB 模块在未连接数据库的时候抛出未知异常
|
||||
- 修复部分情况下打包模块出现的错误
|
||||
- 修复 OneBot 事件无法响应的 bug
|
||||
- 新增部分 EventDispatcher 触发的事件 debug 日志
|
||||
|
||||
## v2.5.2 (build 418)
|
||||
|
||||
> 更新时间:2021.9.10
|
||||
|
||||
- 新增 AnnotationException,统一框架内部的抛出异常的类型
|
||||
- 新增 AnnotationParser 下的 `verifyMiddlewares()` 方法
|
||||
- 私有化 CQAPI 类下的内部方法
|
||||
- 将 WebSocket API 响应超时时间从 60 秒缩短为 30 秒
|
||||
- 修复 DB 类不能使用旧查询器的 bug
|
||||
- 统一 DB 类下抛出 Exception 的类型为 ZMException 的子类
|
||||
- EventDispatcher 新增对 `middleware_error_policy` 的处理段
|
||||
- 配置文件下 `runtime` 新增 `middleware_error_policy` 字段
|
||||
- 将 LightCache 组件抛出的异常改为 LightCacheException
|
||||
- ModuleManager 修复改配置的 `load_path` 不生效的 bug
|
||||
- 修复打包时生成的 Phar Autoload 列表出错的 bug
|
||||
- 将配置的 override 改为 overwrite
|
||||
- 新增解包时忽略依赖的选项(`--ignore-depends`)
|
||||
- 删除众多调试日志,修改部分调试日志为 debug 级别的输出
|
||||
- 修改 `ZM\MySQL\MySQLManager` 下的 `getConnection()` 为 `getWrapper()`
|
||||
- MySQLPool 对象新增 `getCount()` 方法
|
||||
- 新增 MySQLQueryBuilder 类(`doctrine/dbal` 的 wrapper 类)
|
||||
- 修复 MySQLStatement 封装原 dbal 组件时与连接池不兼容的 bug
|
||||
- 新增 MySQLStatementWrapper 类
|
||||
- 完善 MySQLWrapper 类,用作主要的查询对象控制类
|
||||
- 编写外部插件加载方式(Phar 热加载功能)
|
||||
- 修复 `ZMUtil::getClassesPsr4()` 方法在遇到空扩展名文件时的报错
|
||||
|
||||
## v2.5.1 (build 416)
|
||||
|
||||
> 更新时间:2021.7.9
|
||||
|
||||
- 修复:脚手架无法正常使用 `init` 命令的 bug。
|
||||
|
||||
## v2.5.0(build 415)
|
||||
|
||||
> 更新时间:2021.7.9
|
||||
|
||||
以下是版本**新增内容**:
|
||||
|
||||
- 新增全新的模块系统,可打包模块(src 目录下的子目录用户逻辑代码)为 phar 格式进行分发和版本备份。
|
||||
- 全局配置文件新增 `module_loader` 项,用于配置外部模块加载的一些设置。
|
||||
- 全局配置文件新增 `runtime` 配置项,可自定义配置 Swoole 的一些运行时参数,目前可配置一键协程化的 Hook 参数和 Swoole Server 的启动模式。
|
||||
- 新增 `module:list` 命令,用于查看未打包和已打包的模块列表。
|
||||
- 新增 `module:pack` 命令,用于打包现有 src 目录下的模块。
|
||||
- 新增 `module:unpack` 命令,用于解包现有的 phar 模块包。
|
||||
- 新增打包框架功能,支持将用户的整个项目连同炸毛框架打包为一个 phar 便携运行,使用命令 `build`。
|
||||
- 新增快捷脚本 `./zhamao`,效果同 `vendor/bin/start` 或 `bin/start`。
|
||||
- 新增启动参数 `--interact`:又重新支持交互终端了,但还是有点问题,不推荐使用。
|
||||
- 新增启动参数 `--disable-safe-exit`:如果你的项目在 Ctrl+C 时总是卡住且项目内没有什么使用 LightCache 等缓存在内存的数据可开启防止关不掉框架。
|
||||
- 新增启动参数 `--preview`:只显示参数,不启动炸毛框架的服务器。
|
||||
- 新增启动参数 `--force-load-module`:强制打包状态下加载的模块(使用英文逗号分隔多个模块名称)。
|
||||
- `CoroutinePool` 协程池新增 `getRunningCoroutineCount` 方法,用于查看协程池中的协程数量。
|
||||
- `DataProvider` 新增 `getFrameworkRootDir()`、`getSourceRootDir()`,分别代表获取框架的根目录和用户源码根目录。(详见下方对目录的定义解释)
|
||||
- `DataProvider` 中 `getDataFolder` 新增参数 `$second = ''`,如果给定,则自动创建子目录 `$second` 并返回。
|
||||
- `DataProvider` 新增 `scanDirFiles()` 方法,用于扫描目录,可选择是否递归、是否返回相对路径,也支持扫描 Phar 文件内的路径,非常好用。
|
||||
- `DataProvider` 新增 `isRelativePath()` 方法,检查路径是否为相对路径(根据第一个字符是否是 '/' 来判断)。
|
||||
- `ZMUtil` 新增 `getClassesPsr4()` 方法,用于根据 Psr-4 标准来获取目录下的所有类文件。
|
||||
- 新增全局错误码,可以根据错误码在文档内快速定位和解决问题。
|
||||
- 中间件和注解事件支持回溯,可以快速查看调用栈(比如中间件可以知道自己是在哪个注解事件中被调用)。
|
||||
- 使用 `./zhamao build` 来构建框架的 phar 包时增加显示进度条。
|
||||
- EventDispatcher 新增方法 `getEid()` 和 `getClass()`,分别用于获取事件分发 ID 和注解事件的注解类名称。
|
||||
- 新增 EventTracer,用于追踪事件的调用栈。
|
||||
- 中间件支持传参。
|
||||
- MySQL 数据库查询器改为使用 `doctrine/dbal` 组件,更灵活和稳定。
|
||||
- 新增对 `SWOOLE_BASE` 模式的支持(支持只启动一个进程的 Server)。
|
||||
|
||||
以下是版本**修改内容**:
|
||||
|
||||
- 启动文件 `vendor/bin/start` 修改为 shell 脚本,可自动寻找 PHP 环境。
|
||||
- 全局强制依赖 `league/climate` 组件。
|
||||
- 修复框架启动时的信息显示换行问题。
|
||||
- 修复框架使用 Phar 方式启动时导致的报错。
|
||||
- 修复使用 Ctrl+C 结束时一部分用户卡住的 bug。
|
||||
- 远程和本地终端去掉 stop 命令,建议直接使用发 SIGTERM 方式结束框架。
|
||||
- 全局配置文件的 `zm_data` 根目录默认修改为 `WORKING_DIR`。
|
||||
- 命令 `systemd:generate` 修改为 `generate:systemd`。
|
||||
- 全局配置文件删除 `server_event_handler_class` 项,此项废弃。
|
||||
- 修复部分 CQ 码解析过程中没有转义的问题。
|
||||
- 将 `ZMRobot` 类转移为 `OneBotV11` 类,但提供兼容。
|
||||
- 修复在守护进程模式下使用 `daemon:reload` 和 `daemon:stop` 命令可能失效的问题。
|
||||
- 修复 systemd 生成时脚本目录错误的 bug。
|
||||
- 修复 PipeMessage 等事件未捕获错误导致崩溃的问题。
|
||||
- `ZM\Http\RouteManager` 移动到 `ZM\Utils\Manager\RouteManager`,但原地址兼容。
|
||||
- 修复 `Terminal` 类使用的一些问题。
|
||||
- 对 `pcntl` 扩展改为可选依赖,当 Swoole 版本大于等于 4.6.7 时不需要安装 `pcntl` 扩展。
|
||||
- 修正启动时框架对缺省配置项的一些默认参数。
|
||||
- 注解 `@OnSetup` 和 `@SwooleHandler` 可直接使用,无需设置 `server_event_handler_class` 即可。
|
||||
- 修复框架在一些非正常终端中运行时导致错误的问题。
|
||||
- 使用 `--debug-mode` 参数时,自动开启热更新。
|
||||
- 修复脚手架在使用 composer 更新后检查全局配置功能的 bug。
|
||||
- 修复重启和关闭框架时造成的非正常连接断开。
|
||||
- 改用独立进程监听文件变化和终端输入。
|
||||
- 修复有协程中断的任务时停止服务器会报 Swoole 警告的 bug。
|
||||
- 修复连接被反复断开的问题。
|
||||
|
||||
**对目录的定义解释**:
|
||||
|
||||
在 2.4.4 版本之前,使用炸毛框架中,只含有两种目录,`getWorkingDir` 和 `getDataFolder`,分别代表获取工作目录和数据目录。在 2.5 版本中,又新增了 `getFrameworkRootDir` 代表获取框架的根目录,`getSourceRootDir` 代表获取源码的根目录。
|
||||
|
||||
以 Composer 运行模式举例,如果你使用 `composer create-project zhamao/framework-starter` 命令新建的框架,那么假设我们从 `/app` 目录下运行此命令,然后使用 `cd framework-starter/` 进入项目目录,此时我们使用 `vendor/bin/start server` 命令运行服务器,对应的目录为:
|
||||
|
||||
- `WorkingDir`:`/app/framework-starter/`
|
||||
- `SourceRootDir`:`/app/framework-starter/`
|
||||
- `FrameworkRootDir`:`/app/framework-starter/vendor/zhamao/framework/`
|
||||
|
||||
如果以源码模式(直接克隆 `zhamao-framework.git` 仓库),启动框架,那么使用命令 `bin/start server` 启动框架后,以上三个返回的目录则完全相同。
|
||||
|
||||
如果以 2.5 版本新的项目归档模式(build)启动框架,假设我们的项目代码打包为 `server.phar`,在 `/app/` 目录,我们使用命令 `php server.phar server` 启动炸毛框架,那么它对应的目录为:
|
||||
|
||||
- `WorkingDir`:`/app/`
|
||||
- `SourceRootDir`:`phar:///app/server.phar/`
|
||||
- `FrameworkRootDir`:`phar:///app/server.phar/vendor/zhamao/framework/`
|
||||
|
||||
如果最后一种归档方式启动的框架是从源码模式打包而来,那么 `FrameworkRootDir` 就与 `SourceRootDir` 相同。
|
||||
|
||||
**版本部分兼容问题变化**:
|
||||
|
||||
理论上如果不使用框架内部未开放的接口方法的话,从 2.4 升级到 2.5 是非常自然的,但是也有一部分可能会造成不兼容的问题。
|
||||
|
||||
- 生成 systemd 配置文件的命令 `systemd:generate` 变成 `generate:systemd`。
|
||||
- 全局配置文件中的 `zm_data` 的父目录由 `__DIR__ . "/../"` 改为 `WORKING_DIR`。
|
||||
- 2.5 版本将 ZMRobot 类中的所有函数方法都移动到了 `OneBotV11` 类中,但原先的 ZMRobot 还可以使用。
|
||||
|
||||
## v2.4.4 (build 405)
|
||||
|
||||
> 更新时间:2021.3.29
|
||||
|
||||
以下是可能不兼容的变更:
|
||||
|
||||
- 新增依赖:框架需要 PHP 安装 pcntl 扩展以及开启 `pcntl_signal` 函数(一般情况下编译安装的都会有,宝塔面板请手动解除函数禁用)
|
||||
|
||||
## v2.4.3 (build 403)
|
||||
|
||||
> 更新时间:2021.3.29
|
||||
|
||||
- 新增:swoole 设置配置新增 `max_wait_time` 项,设置等待进程关闭流程最大时间(秒)
|
||||
- 新增:常量 `MAIN_WORKER`,值等同于 `worker_cache` 项中的 `worker` 参数(WorkerCache 所在的进程)
|
||||
- 新增:`LightCache` 新增 `getExpireTS()` 方法,用于返回项目过期的时间戳
|
||||
- 修复:`savePersistence()` 的部分丢失数据的 bug
|
||||
- 新增:全局方法 `zm_go()`
|
||||
- 修复:2.4.2 版本下的刷屏报错
|
||||
- 优化:Ctrl+C 响应机制,启用异步 重启/关闭 措施,防止残留僵尸进程和丢失数据
|
||||
|
||||
## v2.4.2 (build 402)
|
||||
|
||||
> 更新时间:2021.3.27
|
||||
|
||||
- 更改:`WORKING_DIR` 常量的含义
|
||||
- 修复:未指定 `--remote-terminal` 参数时还依旧开启远程终端的 bug
|
||||
- 删除:`phar_classloader()` 全局方法
|
||||
- 更改:持久化存储 LightCache 的逻辑,修复一个愚蠢的容易造成误用的方式
|
||||
- 新增:LightCache 方法 `addPersistence()` 和 `removePersistence()`
|
||||
- 新增:框架启动短指令 `./zhamao` 或 `php zhamao`
|
||||
|
||||
## 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/start 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 数据库查询导致的一系列问题
|
||||
- 修复:内存泄露问题
|
||||
|
||||
> 2.3.2-2.3.4 版本由于操作失误导致代码不完整,请直接使用 2.3.5 即可。
|
||||
|
||||
## v2.3.1
|
||||
|
||||
> 更新时间:2021.3.18
|
||||
|
||||
- 规范代码,修复一个小报错的 bug
|
||||
|
||||
## v2.3.0
|
||||
|
||||
> 更新时间:2021.3.16
|
||||
|
||||
- 新增:MessageUtil 消息处理工具类
|
||||
- 新增:TaskManager,封装了 TaskWorker 进程的应用
|
||||
- 新增:CQObject,使用 `CQ::getCQ()` 可获取对象形式的 CQ 码解析结果
|
||||
- 新增:`@OnTask` 注解,绑定任务函数
|
||||
- 新增:RouteManager 路由管理类,可快速添加路由
|
||||
- 修复:`ZM_DATA` 和 `DataProvider::getDataFolder()` 返回 false 的问题
|
||||
- 优化:关闭显示停止框架后多余的输出信息
|
||||
|
||||
注:本次升级建议升级后合并全局配置文件,有一些新加的内容。
|
||||
|
||||
## v2.2.11
|
||||
|
||||
> 更新时间:2021.3.13
|
||||
|
||||
- 新增:内部 ID 版本号(ZM_VERSION_ID)
|
||||
- 优化:启动时 log 的等级
|
||||
- 移除:终端输入命令
|
||||
- 修复:纯 HTTP 服务器的启动 bug
|
||||
- 新增:`zm_timer` 的报错处理,防止服务器直接崩掉
|
||||
|
||||
## 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
|
||||
|
||||
- 更新: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
|
||||
|
||||
> 更新时间:2021.2.7
|
||||
|
||||
- 修复:终端交互导致的 ssh 断掉后 CPU 占用过高的问题
|
||||
- 修复:WorkerCache 在缺少配置文件下工作异常的问题
|
||||
- 新增:全局函数:`zm_atomic()`
|
||||
|
||||
## v2.2.3
|
||||
|
||||
> 更新时间:2021.1.30
|
||||
@@ -110,4 +544,4 @@
|
||||
|
||||
> 更新时间:2020.12.23
|
||||
|
||||
已发布正式版。
|
||||
已发布正式版。
|
||||
|
||||
34
ext/go-cqhttp/go-cqhttp-down.sh
Normal file
34
ext/go-cqhttp/go-cqhttp-down.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "正在检查最新版本的go-cqhttp..."
|
||||
|
||||
if [ "$(uname -m)" = "x86_64" ]; then
|
||||
arch_type="amd64"
|
||||
elif [ "$(uname -m)" = "i386" ]; then
|
||||
arch_type="386"
|
||||
elif [ "$(uname -m)" = "aarch64" ]; then
|
||||
arch_type="arm64"
|
||||
else
|
||||
echo "Not supported architecture: $(uname -m)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
aas=$(uname -s | tr 'A-Z' 'a-z')
|
||||
|
||||
ver=$(wget -qO- -t1 -T2 "https://fgit-api.zhamao.me/repos/Mrs4s/go-cqhttp/releases" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
||||
|
||||
if [ "$ver" != "" ]; then
|
||||
echo "最新版本:"$ver
|
||||
echo -n "是否下载到本地?[y/N] "
|
||||
read option
|
||||
if [ "$option" = "y" ]; then
|
||||
wget https://fgit.zhamao.me/Mrs4s/go-cqhttp/releases/download/$ver/go-cqhttp-$ver-$aas-$arch_type.tar.gz -O temp.tar.gz
|
||||
if [ $? != 0 ]; then
|
||||
wget https://fgit.zhamao.me/Mrs4s/go-cqhttp/releases/download/$ver/go-cqhttp_"$aas""_""$arch_type"".tar.gz" -O temp.tar.gz
|
||||
fi
|
||||
tar -zxvf temp.tar.gz go-cqhttp
|
||||
rm temp.tar.gz
|
||||
echo "下载完成,启动命令:./go-cqhttp"
|
||||
echo "首次启动后先编辑config文件!"
|
||||
fi
|
||||
fi
|
||||
58
install-runtime.sh
Executable file
58
install-runtime.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function download_file() {
|
||||
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
|
||||
echo $1
|
||||
$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 test_composer_and_php() {
|
||||
succ=$("$(pwd)/runtime/composer" -n about | grep Manage)
|
||||
if [ "$succ" = "" ]; then
|
||||
echo "Download PHP binary and composer failed!"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
mkdir "$(pwd)/runtime" >/dev/null 2>&1
|
||||
if [ ! -f "$(pwd)/runtime/php" ]; then
|
||||
download_file "https://dl.zhamao.me/php-bin/down.php?php_ver=7.4&arch=$(uname -m)" "$(pwd)/runtime/php.tar.gz"
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
tar -xf "$(pwd)/runtime/php.tar.gz" -C "$(pwd)/runtime/"
|
||||
fi
|
||||
if [ ! -f "$(pwd)/runtime/composer" ]; then
|
||||
download_file "https://mirrors.aliyun.com/composer/composer.phar" "$(pwd)/runtime/composer.phar"
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo '$(dirname $0)/php $(dirname $0)/composer.phar $@' > $(pwd)/runtime/composer
|
||||
chmod +x $(pwd)/runtime/composer
|
||||
test_composer_and_php
|
||||
fi
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "成功下载!" && \
|
||||
echo -e "PHP使用:\truntime/php -v" && \
|
||||
echo -e "Composer使用:\truntime/composer"
|
||||
26
instant-demo.php
Normal file
26
instant-demo.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
use ZM\Module\InstantModule;
|
||||
use ZM\ZMServer;
|
||||
|
||||
$weather = new InstantModule('weather');
|
||||
|
||||
$weather->onEvent(OnOpenEvent::class, ['connect_type' => 'qq'], function (ConnectionObject $conn) {
|
||||
Console::info('机器人 ' . $conn->getOption('connect_id') . ' 已连接!');
|
||||
});
|
||||
|
||||
$weather->onEvent(CQCommand::class, ['match' => '你好'], function () {
|
||||
ctx()->reply('hello呀!');
|
||||
});
|
||||
|
||||
$app = new ZMServer('app-name');
|
||||
$app->addModule($weather);
|
||||
$app->run();
|
||||
99
mkdocs.yml
99
mkdocs.yml
@@ -1,4 +1,4 @@
|
||||
site_name: 炸毛框架 v2
|
||||
site_name: 炸毛框架文档
|
||||
|
||||
repo_name: '炸毛框架'
|
||||
repo_url: 'https://github.com/zhamao-robot/zhamao-framework'
|
||||
@@ -10,31 +10,36 @@ theme:
|
||||
favicon: assets/favicon.png
|
||||
language: zh
|
||||
palette:
|
||||
primary: blue
|
||||
accent: blue
|
||||
primary: indigo
|
||||
accent: indigo
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.sections
|
||||
extra_javascript:
|
||||
- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js
|
||||
- javascripts/library/highlight.min.js
|
||||
- javascripts/config.js
|
||||
extra_css:
|
||||
- assets/css/extra.css
|
||||
- https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/default.min.css
|
||||
- assets/css/library/default.min.css
|
||||
plugins:
|
||||
- search:
|
||||
lang: ja
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- pymdownx.details
|
||||
- abbr
|
||||
- pymdownx.highlight:
|
||||
linenums: true
|
||||
linenums_style: pymdownx.inline
|
||||
extra:
|
||||
version:
|
||||
method: mike
|
||||
provider: mike
|
||||
|
||||
copyright: 'Copyright © 2019 - 2020 CrazyBot Team <span class="tx-switch">
|
||||
copyright: 'Copyright © 2019 - 2022 CrazyBot Team <span class="tx-switch">
|
||||
<button data-md-color-scheme="default"><code>默认模式</code></button>
|
||||
<button data-md-color-scheme="slate"><code>暗黑模式</code></button>
|
||||
</span>
|
||||
@@ -50,7 +55,7 @@ copyright: 'Copyright © 2019 - 2020 CrazyBot Team &n
|
||||
name.textContent = attr;
|
||||
})
|
||||
})
|
||||
</script><br><a href="http://beian.miit.gov.cn">蒙ICP备18000198号-1</a>'
|
||||
</script><br><a href="http://beian.miit.gov.cn">沪ICP备2021010446号-1</a>'
|
||||
|
||||
nav:
|
||||
- 指南:
|
||||
@@ -62,6 +67,8 @@ nav:
|
||||
- 基本配置: guide/basic-config.md
|
||||
- 编写模块: guide/write-module.md
|
||||
- 注册事件响应: guide/register-event.md
|
||||
- 升级指南: guide/upgrade.md
|
||||
- 错误码对照表: guide/errcode.md
|
||||
- 事件和注解:
|
||||
- 事件和注解: event/index.md
|
||||
- 机器人注解事件: event/robot-annotations.md
|
||||
@@ -72,31 +79,65 @@ nav:
|
||||
- 事件分发器: event/event-dispatcher.md
|
||||
- 框架组件:
|
||||
- 框架组件: component/index.md
|
||||
- 机器人 API: component/robot-api.md
|
||||
- CQ 码(多媒体消息): component/cqcode.md
|
||||
- 上下文: component/context.md
|
||||
- 聊天机器人组件:
|
||||
- 机器人动作(V12): component/bot/robot-api-12.md
|
||||
- 机器人动作(V11): component/bot/robot-api.md
|
||||
- CQ 码(多媒体消息): component/bot/cqcode.md
|
||||
- 机器人消息处理: component/bot/message-util.md
|
||||
- Token 验证: component/bot/access-token.md
|
||||
- 图灵机器人 API: component/bot/turing-api.md
|
||||
- 存储:
|
||||
- LightCache 轻量缓存: component/light-cache.md
|
||||
- MySQL 数据库: component/mysql.md
|
||||
- Redis 数据库: component/redis.md
|
||||
- ZMAtomic 原子计数器: component/atomics.md
|
||||
- SpinLock 自旋锁: component/spin-lock.md
|
||||
- 协程池: component/coroutine-pool.md
|
||||
- 单例类: component/singleton-trait.md
|
||||
- ZMUtil 杂项: component/zmutil.md
|
||||
- 全局方法: component/global-functions.md
|
||||
- HTTP 和 WebSocket 客户端: component/zmrequest.md
|
||||
- Console 终端: component/console.md
|
||||
- LightCache 轻量缓存: component/store/light-cache.md
|
||||
- MySQL 查询器:
|
||||
- 简介: component/store/mysql/mysql.md
|
||||
- 配置: component/store/mysql/config.md
|
||||
- 执行 SQL 语句: component/store/mysql/common-query.md
|
||||
- MySQL 查询器(废弃): component/store/mysql-db.md
|
||||
- Redis 数据库: component/store/redis.md
|
||||
- ZMAtomic 原子计数器: component/store/atomics.md
|
||||
- SpinLock 自旋锁: component/store/spin-lock.md
|
||||
- 文件管理: component/store/data-provider.md
|
||||
- 通用组件:
|
||||
- 上下文: component/common/context.md
|
||||
- 协程池: component/common/coroutine-pool.md
|
||||
- 单例类: component/common/singleton-trait.md
|
||||
- ZMUtil 杂项: component/common/zmutil.md
|
||||
- 全局方法: component/common/global-functions.md
|
||||
- Console 终端: component/common/console.md
|
||||
- TaskWorker 管理: component/common/task-worker.md
|
||||
- Terminal 终端: component/common/remote-terminal.md
|
||||
- EventTracer 事件追踪器: component/common/event-tracer.md
|
||||
- HTTP 服务器工具类:
|
||||
- HTTP 和 WebSocket 客户端: component/http/zmrequest.md
|
||||
- HTTP 路由管理: component/http/route-manager.md
|
||||
- 模块/插件管理:
|
||||
- 模块打包: component/module/module-pack.md
|
||||
- 模块解包: component/module/module-unpack.md
|
||||
- 进阶开发:
|
||||
- 进阶开发: advanced/index.md
|
||||
- 框架剖析: advanced/framework-structure.md
|
||||
- 框架启动模式: advanced/custom-start.md
|
||||
- 从 v1 升级: advanced/to-v2.md
|
||||
- 内部类文件手册: advanced/inside-class.md
|
||||
- 接入 WebSocket 客户端: advanced/connect-ws-client.md
|
||||
- 框架多进程: advanced/multi-process.md
|
||||
- FAQ: FAQ.md
|
||||
- 框架高级开发:
|
||||
- 框架剖析: advanced/framework-structure.md
|
||||
- 框架启动模式: advanced/custom-start.md
|
||||
- 手动安装环境: advanced/manually-install.md
|
||||
- 内部类文件手册: advanced/inside-class.md
|
||||
- 框架多进程: advanced/multi-process.md
|
||||
- TaskWorker 提高并发: advanced/task-worker.md
|
||||
- 开发实战教程:
|
||||
- 接入 WebSocket 客户端: advanced/connect-ws-client.md
|
||||
- 编写管理员才能触发的功能: advanced/example/admin.md
|
||||
- 接入青云客聊天API: advanced/example/integrate-qingyunke-chatbot.md
|
||||
- 开发天气机器人: advanced/example/weather-bot.md
|
||||
- FAQ:
|
||||
- FAQ: faq/FAQ.md
|
||||
- 从 v1 升级: faq/to-v2.md
|
||||
- 框架常见问题(持续更新): faq/usual-question.md
|
||||
- 启动时报错 Address already in use: faq/address-already-in-use.md
|
||||
- 出现 deadlock 字样: faq/display-deadlock.md
|
||||
- 使用 LightCache 关闭时无法正常保存持久化: faq/light-cache-wrong.md
|
||||
- CQBefore 过滤不了 waitMessage: faq/wait-message-cqbefore.md
|
||||
- 更新日志:
|
||||
- 更新日志(v2): update/v2.md
|
||||
- 更新日志(v1): update/v1.md
|
||||
- 更新日志(build update): update/build-update.md
|
||||
- 配置文件更新日志: update/config.md
|
||||
- <u>炸毛框架 v1</u>: https://docs-v1.zhamao.xin/
|
||||
|
||||
7
phpstan.neon
Normal file
7
phpstan.neon
Normal file
@@ -0,0 +1,7 @@
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
level: 0
|
||||
paths:
|
||||
- ./src/
|
||||
ignoreErrors:
|
||||
- '#Used constant OS_TYPE_(LINUX|WINDOWS) not found#'
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 385 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 115 KiB |
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Custom\Annotation;
|
||||
|
||||
use Attribute;
|
||||
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\CustomAnnotation;
|
||||
@@ -10,11 +13,17 @@ use ZM\Annotation\Interfaces\CustomAnnotation;
|
||||
/**
|
||||
* Class CustomAnnotation
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor()
|
||||
* @Target("ALL")
|
||||
* @package Custom\Annotation
|
||||
*/
|
||||
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_ALL)]
|
||||
class Example extends AnnotationBase implements CustomAnnotation
|
||||
{
|
||||
/** @var string */
|
||||
public $str = '';
|
||||
|
||||
public function __construct($str = '')
|
||||
{
|
||||
$this->str = $str;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<?php /** @noinspection PhpFullyQualifiedNameUsageInspection */ #plain
|
||||
<?php
|
||||
|
||||
//这里写你的全局函数
|
||||
function pgo(callable $func, $name = "default") {
|
||||
\ZM\Utils\CoroutinePool::go($func, $name);
|
||||
declare(strict_types=1);
|
||||
|
||||
use ZM\Utils\CoroutinePool;
|
||||
|
||||
function pgo(callable $func, string $name = 'default')
|
||||
{
|
||||
CoroutinePool::go($func, $name);
|
||||
}
|
||||
|
||||
@@ -1,48 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\CQ\CQMessage;
|
||||
use ZM\Annotation\Http\Middleware;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Swoole\OnCloseEvent;
|
||||
use ZM\Annotation\Swoole\OnOpenEvent;
|
||||
use ZM\Annotation\Swoole\OnRequestEvent;
|
||||
use ZM\API\CQ;
|
||||
use ZM\API\OneBotV11;
|
||||
use ZM\API\TuringAPI;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Class Hello
|
||||
* @package Module\Example
|
||||
* @since 2.0
|
||||
*/
|
||||
class Hello
|
||||
{
|
||||
/*
|
||||
* 默认的图片监听路由对应目录,如需要使用可取消下面的注释,把上面的 /* 换成 /**
|
||||
* @OnStart(-1)
|
||||
*/
|
||||
// public function onStart() {
|
||||
// \ZM\Http\RouteManager::addStaticFileRoute("/images/", \ZM\Utils\DataProvider::getWorkingDir()."/zm_data/images/");
|
||||
// }
|
||||
|
||||
/**
|
||||
* 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ
|
||||
* @CQCommand(".reload",user_id=627577391)
|
||||
*/
|
||||
public function reload() {
|
||||
ctx()->reply("重启中...");
|
||||
public function reload()
|
||||
{
|
||||
ctx()->reply('重启中...');
|
||||
ZMUtil::reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQCommand("我是谁")
|
||||
*/
|
||||
public function whoami() {
|
||||
$user = ctx()->getRobot()->getLoginInfo();
|
||||
return "你是" . $user["data"]["nickname"] . ",QQ号是" . $user["data"]["user_id"];
|
||||
public function whoami()
|
||||
{
|
||||
$bot = ctx()->getRobot()->getLoginInfo();
|
||||
$bot_id = $bot['data']['user_id'];
|
||||
$r = OneBotV11::get($bot_id);
|
||||
$QQid = ctx()->getUserId();
|
||||
$nick = $r->getStrangerInfo($QQid)['data']['nickname'];
|
||||
return '你是' . $nick . ',QQ号是' . $QQid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向机器人发送"你好啊",也可回复这句话
|
||||
* @CQCommand(match="你好",alias={"你好啊","你是谁"})
|
||||
*/
|
||||
public function hello() {
|
||||
return "你好啊,我是由炸毛框架构建的机器人!";
|
||||
public function hello()
|
||||
{
|
||||
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'] . '」';
|
||||
}
|
||||
|
||||
/**
|
||||
* 图灵机器人的内置实现,在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 = ''; // 请在这里填入你的图灵机器人的apikey
|
||||
if ($api === '') {
|
||||
return false;
|
||||
} // 如果没有填入apikey则此功能关闭
|
||||
if (($this->_running_annotation ?? null) instanceof CQCommand) {
|
||||
$msg = ctx()->getFullArg('我在!有什么事吗?');
|
||||
} else {
|
||||
$msg = ctx()->getMessage();
|
||||
}
|
||||
ctx()->setMessage($msg);
|
||||
if (MessageUtil::matchCommand($msg, ctx()->getData())->status === false) {
|
||||
return TuringAPI::getTuringMsg($msg, $user_id, $api);
|
||||
}
|
||||
QQBot::getInstance()->handle(ctx()->getData(), ctx()->getCache('level') + 1);
|
||||
// 执行嵌套消息,递归层级+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);
|
||||
Console::info(ctx()->getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,15 +135,16 @@ class Hello
|
||||
* @CQCommand(pattern="*从*到*的随机数")
|
||||
* @return string
|
||||
*/
|
||||
public function randNum() {
|
||||
public function randNum()
|
||||
{
|
||||
// 获取第一个数字类型的参数
|
||||
$num1 = ctx()->getNumArg("请输入第一个数字");
|
||||
$num1 = ctx()->getNumArg('请输入第一个数字');
|
||||
// 获取第二个数字类型的参数
|
||||
$num2 = ctx()->getNumArg("请输入第二个数字");
|
||||
$num2 = ctx()->getNumArg('请输入第二个数字');
|
||||
$a = min(intval($num1), intval($num2));
|
||||
$b = max(intval($num1), intval($num2));
|
||||
// 回复用户结果
|
||||
return "随机数是:" . mt_rand($a, $b);
|
||||
return '随机数是:' . mt_rand($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,8 +152,9 @@ class Hello
|
||||
* @RequestMapping("/httpTimer")
|
||||
* @Middleware("timer")
|
||||
*/
|
||||
public function timer() {
|
||||
return "This page is used as testing TimerMiddleware! Do not use it in production.";
|
||||
public function timer()
|
||||
{
|
||||
return 'This page is used as testing TimerMiddleware! Do not use it in production.';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,8 +162,9 @@ class Hello
|
||||
* @RequestMapping("/index")
|
||||
* @RequestMapping("/")
|
||||
*/
|
||||
public function index() {
|
||||
return "Hello Zhamao!";
|
||||
public function index()
|
||||
{
|
||||
return 'Hello Zhamao!';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,8 +173,9 @@ class Hello
|
||||
* @param $param
|
||||
* @return string
|
||||
*/
|
||||
public function paramGet($param) {
|
||||
return "Hello, ".$param["name"];
|
||||
public function paramGet($param)
|
||||
{
|
||||
return 'Hello, ' . $param['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,24 +183,27 @@ class Hello
|
||||
* @OnOpenEvent("qq")
|
||||
* @param $conn
|
||||
*/
|
||||
public function onConnect(ConnectionObject $conn) {
|
||||
Console::info("机器人 " . $conn->getOption("connect_id") . " 已连接!");
|
||||
public function onConnect(ConnectionObject $conn)
|
||||
{
|
||||
Console::info('机器人 ' . $conn->getOption('connect_id') . ' 已连接!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 在机器人断开连接后向终端输出信息
|
||||
* @OnCloseEvent("qq")
|
||||
* @param ConnectionObject $conn
|
||||
*/
|
||||
public function onDisconnect(ConnectionObject $conn) {
|
||||
Console::info("机器人 " . $conn->getOption("connect_id") . " 已断开连接!");
|
||||
public function onDisconnect(ConnectionObject $conn)
|
||||
{
|
||||
Console::info('机器人 ' . $conn->getOption('connect_id') . ' 已断开连接!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
|
||||
* @OnRequestEvent(rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
||||
* @throws InterruptException
|
||||
*/
|
||||
public function onRequest() {
|
||||
public function onRequest()
|
||||
{
|
||||
EventDispatcher::interrupt();
|
||||
}
|
||||
|
||||
@@ -122,8 +211,9 @@ class Hello
|
||||
* 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改
|
||||
* @OnOpenEvent("default")
|
||||
*/
|
||||
public function closeUnknownConn() {
|
||||
Console::info("Unknown connection , I will close it.");
|
||||
server()->close(ctx()->getConnection()->getFd());
|
||||
public function closeUnknownConn()
|
||||
{
|
||||
Console::info('Unknown connection , I will close it.');
|
||||
server()->disconnect(ctx()->getConnection()->getFd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Module\Middleware;
|
||||
|
||||
use Exception;
|
||||
@@ -13,7 +15,6 @@ use ZM\Http\MiddlewareInterface;
|
||||
/**
|
||||
* Class TimerMiddleware
|
||||
* 示例中间件:用于统计路由函数运行时间用的
|
||||
* @package Module\Middleware
|
||||
* @MiddlewareClass("timer")
|
||||
*/
|
||||
class TimerMiddleware implements MiddlewareInterface
|
||||
@@ -22,9 +23,9 @@ class TimerMiddleware implements MiddlewareInterface
|
||||
|
||||
/**
|
||||
* @HandleBefore()
|
||||
* @return bool
|
||||
*/
|
||||
public function onBefore() {
|
||||
public function onBefore(): bool
|
||||
{
|
||||
$this->starttime = microtime(true);
|
||||
return true;
|
||||
}
|
||||
@@ -32,17 +33,18 @@ class TimerMiddleware implements MiddlewareInterface
|
||||
/**
|
||||
* @HandleAfter()
|
||||
*/
|
||||
public function onAfter() {
|
||||
Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms.");
|
||||
public function onAfter()
|
||||
{
|
||||
Console::info('Using ' . round((microtime(true) - $this->starttime) * 1000, 3) . ' ms.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @HandleException(\Exception::class)
|
||||
* @param Exception $e
|
||||
* @throws Exception
|
||||
*/
|
||||
public function onException(Exception $e) {
|
||||
Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred.");
|
||||
public function onException(Exception $e)
|
||||
{
|
||||
Console::error('Using ' . round((microtime(true) - $this->starttime) * 1000, 3) . ' ms but an Exception occurred.');
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user