change ALL docs from MkDocs to VuePress!!

This commit is contained in:
crazywhalecc
2022-03-22 00:51:03 +08:00
parent c7df37b17c
commit dbd78d4b86
87 changed files with 1358 additions and 1415 deletions

3
docs/component/README.md Normal file
View File

@@ -0,0 +1,3 @@
# 框架组件
这里列举了框架内的你可能会用到的常用组件。

View File

@@ -1,4 +1,4 @@
# Token 验证
# 接入安全验证 - Token
为了保障安全,框架支持给接入的 WebSocket 连接验证 Token如果不设置 Token 同时又将框架的端口暴露在公网将会非常危险。
@@ -27,9 +27,9 @@
go-cqhttp 的配置段:
```hjson
// 访问密钥, 强烈推荐在公网的服务器设置
access_token: "emhhbWFvLXJvYm90"
```
// 访问密钥, 强烈推荐在公网的服务器设置
access_token: "emhhbWFvLXJvYm90"
```
框架的配置文件配置段:
@@ -56,4 +56,4 @@ $config["access_token"] = function($token){
## 自定义验证open 事件)
当然,这里设置了自定义方式,其实你也可以在下一层的 `@OnOpenEvent` 注解事件中进行自定义内容和判断,具体见 `@OnOpenEvent` 的相关章节。
当然,这里设置了自定义方式,其实你也可以在下一层的 `@OnOpenEvent` 注解事件中进行自定义内容和判断,具体见 `@OnOpenEvent` 的相关章节。

View File

@@ -1,4 +1,4 @@
# CQ 码(多媒体消息)
# 多媒体消息 - CQ码
消息中的多媒体内容使用 CQ 码来表示,形如 `[CQ:face,id=178]`。其中,`[CQ:]` 是固定格式;`face` 是「功能名」,除了 `face` 还有许多不同的功能名;`id=178` 是「参数」,某些功能不需要参数,而另一些需要多个参数,当有多个参数时,参数间使用逗号分隔。
@@ -14,11 +14,13 @@
更多 CQ 码功能请参考 [消息段类型](https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md)。
!!! warning "注意"
::: warning 注意
CQ 码中不应有多余的空格,例如不应该使用 `[CQ:face, id=178]`
CQ 码中不应有多余的空格,例如不应该使用 `[CQ:face, id=178]`
CQ 码的参数值可以包含空格、换行、除 `[],&` 之外的特殊符号等。在解析时,应直接取 `[CQ:` 后、第一个 `,``]` 前的部分为功能名,第一个 `,` 之后到 `]` 之间的部分为参数,按 `,` 分割后,每个部分第一个 `=` 前的内容为参数名,之后的部分为参数值。例如 `[CQ:share,title=标题中有=等号,url=http://baidu.com]` 中,功能名为 `share``title` 参数值为 `标题中有=等号``url` 参数值为 `http://baidu.com`
CQ 码的参数值可以包含空格、换行、除 `[],&` 之外的特殊符号等。在解析时,应直接取 `[CQ:` 后、第一个 `,``]` 前的部分为功能名,第一个 `,` 之后到 `]` 之间的部分为参数,按 `,` 分割后,每个部分第一个 `=` 前的内容为参数名,之后的部分为参数值。例如 `[CQ:share,title=标题中有=等号,url=http://baidu.com]` 中,功能名为 `share``title` 参数值为 `标题中有=等号``url` 参数值为 `http://baidu.com`
:::
## 转义
@@ -69,12 +71,12 @@ class Hello {
}
```
效果
效果
<chat-box>
) 发送图片
[ https://zhamao.xin/file/hello.jpg
</chat-box>
<chat-box :my-chats="[
{type:0,content:'发送图片'},
{type:3,content:'https://zhamao.xin/file/hello.jpg'}
]"></chat-box>
## CQ 码操作
@@ -191,15 +193,17 @@ public function faceTest() {
}
```
<chat-box>
) 打盹
( 正在打盹...
[ https://docs-v1.zhamao.xin/face/8.gif
</chat-box>
<chat-box :my-chats="[
{type:0,content:'打盹'},
{type:1,content:'正在打盹...'},
{type:3,content:'https://docs-v1.zhamao.xin/face/8.gif'},
]"></chat-box>
!!! note "提示"
对于不断更新的 QQ 版本下,可能会持续扩充新的 QQ 表情,如果上表没有新的表情的话,也可以使用消息接收的方式,让机器人收到表情后解析出来对应的 id 然后再发送。
::: tip 提示
对于不断更新的 QQ 版本下,可能会持续扩充新的 QQ 表情,如果上表没有新的表情的话,也可以使用消息接收的方式,让机器人收到表情后解析出来对应的 id 然后再发送。
:::
### CQ::image() - 发送图片
@@ -221,7 +225,11 @@ public function faceTest() {
- 绝对路径,例如 `file:///root/imagetest/1.png`,格式使用 [`file` URI](https://tools.ietf.org/html/rfc8089)
- 网络 URL例如 `http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg`
- Base64 编码,例如 `base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==`
- Base64 编码,例如
```
base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==
```
### CQ::record() - 发送语音
@@ -250,10 +258,10 @@ public function say() {
}
```
<chat-box>
) 说你好
( [语音消息,点击收听] 2'' )))
</chat-box>
<chat-box :my-chats="[
{type:0,content:'说你好'},
{type:1,content:'[语音消息,点击收听] 2\'\' )))'},
]"></chat-box>
> 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。
@@ -274,10 +282,10 @@ public function atTest() {
}
```
<chat-box>
) at测试
( @鲸鱼 你好啊!
</chat-box>
<chat-box :my-chats="[
{type:0,content:'at测试'},
{type:1,content:'@鲸鱼 你好啊!'},
]"></chat-box>
### CQ::video() - 发送短视频
@@ -346,9 +354,11 @@ public function atTest() {
匿名发消息。需要在允许匿名发消息的群里发。
!!! tip "提示"
::: tip 提示
当收到匿名消息时,需要通过 [消息事件的群消息](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#群消息) 的 `anonymous` 字段判断。
当收到匿名消息时,需要通过 [消息事件的群消息](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#群消息) 的 `anonymous` 字段判断。
:::
定义:`CQ::anonymous($ignore = 1)`
@@ -515,9 +525,11 @@ public function xmlTest() {
其中 `$resid` 是面向 go-cqhttp 扩展的参数,默认不填为 0走小程序通道填了走富文本通道发送。
!!! tip "提示"
::: tip 提示
因为某些众所周知的原因XML 和 JSON 的返回不提供实例,有兴趣的可以自行研究如何编写,文档不含任何相关教程。
因为某些众所周知的原因XML 和 JSON 的返回不提供实例,有兴趣的可以自行研究如何编写,文档不含任何相关教程。
:::
### CQ::_custom() - 扩展自定义 CQ 码
@@ -536,4 +548,3 @@ public function xmlTest() {
CQ::_custom("at",["qq" => "123456","qwe" => "asd"]);
// 返回:[CQ:at,qq=123456,qwe=asd]
```

View File

@@ -1,4 +1,4 @@
# MessageUtil 消息处理工具类
# 消息处理工具类 - MessageUtil
类定义:`\ZM\Utils\MessageUtil`
@@ -81,9 +81,11 @@ MessageUtil::isAtMe("[CQ:at,qq=123456789]另一个朋友你好","123456"); // fa
返回:数组,切分后的。
!!! tip "为什么不直接使用 explode 呢"
::: tip 为什么不直接使用 explode 呢
因为 `explode()` 只会简单粗暴的切割字符串,假设用户输入的消息中两个词中间有多个空格,则会有空的词出现。例如 `你好 我是一个长空格`。此函数会将多个空格当作一个空格来对待。
因为 `explode()` 只会简单粗暴的切割字符串,假设用户输入的消息中两个词中间有多个空格,则会有空的词出现。例如 `你好 我是一个长空格`。此函数会将多个空格当作一个空格来对待。
:::
```php
MessageUtil::splitCommand("你好 我是傻瓜\n我是傻瓜二号"); // ["你好","我是傻瓜","我是傻瓜二号"]
@@ -123,10 +125,10 @@ public function onStart() {
}
```
<chat-box>
) 炸毛不聪明
( 其实还是很聪明的!
</chat-box>
<chat-box :my-chats="[
{type:0,content:'炸毛不聪明'},
{type:1,content:'其实还是很聪明的!'},
]"></chat-box>
### strToArray()
@@ -176,4 +178,4 @@ $arr = \ZM\Utils\MessageUtil::strToArray($str);
// 我们使用上边的 $arr 作为传入值。
$new_str = \ZM\Utils\MessageUtil::arrayToStr($arr);
// 结果:"你好啊,[CQ:at,qq=123]"
```
```

View File

@@ -1,9 +1,11 @@
# 机器人 APIOneBotV12待发布
# 机器人动作 - V12
!!! tip "提示"
::: tip 提示
目前由于 OneBot 12 标准还没有定稿,处于草案阶段,故框架暂不更新。
在未来升级到 OneBot 12 标准后,框架会提供转换及兼容措施以及 12 版本的 API 方法。
目前由于 OneBot 12 标准还没有定稿,处于草案阶段,故框架暂不更新。
见 [机器人动作OneBot 11](../robot-api)。
在未来升级到 OneBot 12 标准后,框架会提供转换及兼容措施以及 12 版本的 API 方法。
:::
见 [机器人动作OneBot 11](./robot-api.html)。

View File

@@ -1,4 +1,4 @@
# 机器人 APIOneBotV11
# 机器人动作 - V11
OneBotV11 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。
@@ -111,8 +111,9 @@ $obj = $bot->sendGroupMsg("234567", "你好");
echo json_encode($obj, 128|256);
```
输出结果
```json
// 输出结果
{
"status": "ok",
"retcode": 0,
@@ -161,21 +162,18 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
| ------------ | -------------- | ------- |
| `message_id` | number (int32) | 消息 ID |
例子
代码
=== "代码"
```php
$bot = OneBotV11::get(123456); // 123456是你的机器人QQ
$bot->sendPrivateMsg("627577391", "你好啊!你好你好!");
```
```php
$bot = OneBotV11::get(123456); // 123456是你的机器人QQ
$bot->sendPrivateMsg("627577391", "你好啊!你好你好!");
```
=== "效果"
<chat-box>
( 你好啊!你好你好!
</chat-box>
效果
<chat-box :my-chats="[
{type:1,content:'你好啊!你好你好!'}
]"></chat-box>
### sendGroupMsg()
@@ -621,10 +619,11 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
获取 Cookies。
!!! warning "注意"
::: warning 注意
目前开源的 mirai 为底层的机器人客户端均不支持获取 Cookies 和 CSRF Token包括 go-cqhttp。
目前开源的 mirai 为底层的机器人客户端均不支持获取 Cookies 和 CSRF Token包括 go-cqhttp。
:::
参数
@@ -780,9 +779,11 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0)
唯一一个参数做保留,用于选择不同客户端,目前仅支持 `go-cqhttp`,所以缺省也默认为 `go-cqhttp`
!!! warning "注意"
::: warning 注意
由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。
由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。
:::
### callExtendedAPI() (扩充 API
@@ -805,4 +806,3 @@ $result = $bot->callExtendedAPI("get_group_root_files", ["group_id" => 123456]);
var_dump($result["data"]);
// 输出群文件列表
```

View File

@@ -1,4 +1,4 @@
# 图灵机器人 APITuringAPI
# 图灵聊天 - TuringAPI
类定义:`\ZM\API\TuringAPI`
@@ -60,27 +60,27 @@ class Hello {
如上述代码,我们将申请的 apikey 填入变量 `$api` 中,启动机器人即可使用,以下是实测消息(我用自己申请的 key 做测试回复的消息)。
<chat-box>
) 你咋了
( 我没事哦,谢谢您的关心。
) 上海天气
( 上海:周一 03月29日,小雨 东南风转东风,最低气温14度最高气温24度。
^ 切换为群内
) 机器人
( 我在!有什么事吗?
) 你叫啥
( 我的名字叫炸毛,认识你很高兴呢!
</chat-box>
<chat-box :my-chats="[
{type:0,content:'你咋了'},
{type:1,content:'我没事哦,谢谢您的关心。'},
{type:0,content:'上海天气'},
{type:1,content:'上海:周一 03月29日,小雨 东南风转东风,最低气温14度最高气温24度。'},
{type:2,content:'切换为群内'},
{type:0,content:'机器人'},
{type:1,content:'我在!有什么事吗?'},
{type:0,content:'你叫啥'},
{type:1,content:'我的名字叫炸毛,认识你很高兴呢!'},
]"></chat-box>
在默认示例模块中的例子是直接可以拿来用的,这段代码同时做了对 at 的处理、以及兼容用户自定义写的其他命令的方式,下面是默认模块填好 apikey 后可以用的各种方式提问:
<chat-box>
^ 切换为群内
) 我是一条普通消息,这条机器人不会回复我
) @机器人 你叫啥
( 我是聪明可爱的炸毛,认识你很高兴。
) 机器人
( 我在!有什么事吗?
) 一言
( 多少事,从来急,天地转,光阴迫,一万年太久,只争朝夕。
</chat-box>
<chat-box :my-chats="[
{type:2,content:'切换为群内'},
{type:0,content:'我是一条普通消息,这条机器人不会回复我'},
{type:0,content:'@机器人 你叫啥'},
{type:1,content:'我是聪明可爱的炸毛,认识你很高兴。'},
{type:0,content:'机器人'},
{type:1,content:'我在!有什么事吗?'},
{type:0,content:'一言'},
{type:1,content:'多少事,从来急,天地转,光阴迫,一万年太久,只争朝夕。'},
]"></chat-box>

View File

@@ -52,10 +52,11 @@ vendor/bin/start server --log-debug # 以 debug 等级启动框架
输出 warning 级别的 log。
!!! warning 注意
::: warning 注意
框架内出现的用户态异常,比如无法发送 API、无法连接数据库等错误都是 warning 错误,不会导致框架崩溃或功能错误的异常情况建议都使用 warning 输出而不是 error。
框架内出现的用户态异常,比如无法发送 API、无法连接数据库等错误都是 warning 错误,不会导致框架崩溃或功能错误的异常情况建议都使用 warning 输出而不是 error。
:::
### Console::info()
@@ -100,9 +101,11 @@ $str = Console::setColor("I am gold color.", "gold");
炸毛框架支持从终端输入命令来进行一些操作,例如重启框架、停止框架、执行函数等。
!!! warning 注意
::: warning 注意
在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。
在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。
:::
### reload
@@ -174,4 +177,3 @@ vendor/bin/start server --log-theme={主题名}
vendor/bin/start server --log-theme=white-term # 如果用的是白色终端,这个主题更友好
vendor/bin/start server --log-theme=no-color # 如果不想让 log 带有任何颜色,使用无色主题
```

View File

@@ -40,31 +40,33 @@ public function hello() {
可以使用的事件:所有 **getFrame()** 可以使用的,`@OnOpenEvent()``@OnCloseEvent()`
!!! tip "提示"
::: tip 提示
值得注意的是,由于机器人客户端和炸毛框架的连接是通过 WebSocket 进行的,而 WebSocket 是长连接,所以同一个机器人一次连接下收发消息所用的连接是同一个,所以 Fd 也是相同的。同理,炸毛框架的内部来区分多个机器人也是通过这一 Fd 进行判定的。
值得注意的是,由于机器人客户端和炸毛框架的连接是通过 WebSocket 进行的,而 WebSocket 是长连接,所以同一个机器人一次连接下收发消息所用的连接是同一个,所以 Fd 也是相同的。同理,炸毛框架的内部来区分多个机器人也是通过这一 Fd 进行判定的。
=== "代码"
:::
```php
/**
* @CQCommand("测试fd")
*/
public function testfd() {
ctx()->reply("当前机器人连接的fd是".ctx()->getFd()."机器人QQ是".ctx()->getRobotId());
}
```
代码
=== "效果"
```php
/**
* @CQCommand("测试fd")
*/
public function testfd() {
ctx()->reply("当前机器人连接的fd是".ctx()->getFd()."机器人QQ是".ctx()->getRobotId());
}
```
<chat-box>
^ 假设我们和连接55555的机器人的私聊
) 测试fd
( 当前机器人连接的fd是1机器人QQ是55555
^ 假设切到了另一个机器人66666的私聊
) 测试fd
( 当前机器人连接的fd是2机器人QQ是66666
</chat-box>
效果
<chat-box :my-chats="[
{type:2,content:'假设我们和连接55555的机器人的私聊'},
{type:0,content:'测试fd'},
{type:1,content:'当前机器人连接的fd是1机器人QQ是55555'},
{type:2,content:'假设切到了另一个机器人66666的私聊'},
{type:0,content:'测试fd'},
{type:1,content:'当前机器人连接的fd是2机器人QQ是66666'},
]"></chat-box>
## getData() - 获取事件完整数据
@@ -82,11 +84,11 @@ public function onMessage() {
}
```
<chat-box>
^ 假设我是QQ为123456的用户私聊发消息
) 哈咯!!
( 消息类型是private
</chat-box>
<chat-box :my-chats="[
{type:2,content:'假设我是QQ为123456的用户私聊发消息'},
{type:0,content:'哈咯!!'},
{type:1,content:'消息类型是private'},
]"></chat-box>
## getRequest() - HTTP 请求对象
@@ -132,10 +134,10 @@ public function ping() {
ctx()->getRobot()->sendPrivateMsg(123456, "发送私聊消息");
```
<chat-box>
^ 正在和机器人聊天
( 发送私聊消息
</chat-box>
<chat-box :my-chats="[
{type:2,content:'正在和机器人聊天'},
{type:1,content:'发送私聊消息'},
]"></chat-box>
## getMessage() - 获取消息
@@ -143,24 +145,26 @@ ctx()->getRobot()->sendPrivateMsg(123456, "发送私聊消息");
可以使用的事件:`@CQCommand()``@CQMessage``@CQBefore("message")``@CQAfter("message")`
=== "代码"
```php
/**
* @CQMessage(group_id=33333)
*/
public function groupRepeat() {
ctx()->reply(ctx()->getMessage());
}
```
代码
=== "效果"
<chat-box>
^ 现在在群33333内机器人已经成了复读机
) 来世还做复读机!!!
( 来世还做复读机!!!
) 你不许复读!
( 你不许复读!
</chat-box>
```php
/**
* @CQMessage(group_id=33333)
*/
public function groupRepeat() {
ctx()->reply(ctx()->getMessage());
}
```
效果
<chat-box :my-chats="[
{type:2,content:'现在在群33333内机器人已经成了复读机'},
{type:0,content:'来世还做复读机!!!'},
{type:1,content:'来世还做复读机!!!'},
{type:0,content:'你不许复读!'},
{type:1,content:'你不许复读!'},
]"></chat-box>
## getUserId() - 获取用户 QQ 号
@@ -275,16 +279,16 @@ function yourName(){
}
```
<chat-box>
) 自我介绍
( 你叫啥名字呀?
) jerry
( 好的,可爱的机器人记住你叫 jerry 啦!以后多聊天哦!
) 自我介绍
( 你叫啥名字呀?
^ 10分钟没理机器人
( 你都10分钟不理我了嘤嘤嘤
</chat-box>
<chat-box :my-chats="[
{type:0,content:'自我介绍'},
{type:1,content:'你叫啥名字呀?'},
{type:0,content:'jerry'},
{type:1,content:'好的,可爱的机器人记住你叫 jerry 啦!以后多聊天哦!'},
{type:0,content:'自我介绍'},
{type:1,content:'你叫啥名字呀?'},
{type:2,content:'10分钟没理机器人'},
{type:1,content:'你都10分钟不理我了嘤嘤嘤'},
]"></chat-box>
## getArgs() - 自动获取参数
@@ -316,12 +320,12 @@ public function argTest1() {
}
```
<chat-box>
) test
( 请输入你要传入的参数内容
) test2
( 参数内容test2
</chat-box>
<chat-box :my-chats="[
{type:0,content:'test'},
{type:1,content:'请输入你要传入的参数内容'},
{type:0,content:'test2'},
{type:1,content:'参数内容test2'},
]"></chat-box>
`getArgs()` 也有三层封装,在使用过程中避免麻烦的话,推荐使用下面这几种 `get*Arg()` 方式。
@@ -341,14 +345,14 @@ public function argTest1() {
}
```
<chat-box>
) test abc def argtest
( 参数内容abc def argtest
) test
( 请输入你要传入的参数内容
) abc def
( 参数内容abc def
</chat-box>
<chat-box :my-chats="[
{type:0,content:'test abc def argtest'},
{type:1,content:'参数内容abc def argtest'},
{type:0,content:'test'},
{type:1,content:'请输入你要传入的参数内容'},
{type:0,content:'abc def'},
{type:1,content:'参数内容abc def'},
]"></chat-box>
## getNextArg()
@@ -364,14 +368,14 @@ public function argTest1() {
}
```
<chat-box>
) test abc def argtest
( 参数内容abc
) test
( 请输入你要传入的参数内容
) abc
( 参数内容abc
</chat-box>
<chat-box :my-chats="[
{type:0,content:'test abc def argtest'},
{type:1,content:'参数内容abc'},
{type:0,content:'test'},
{type:1,content:'请输入你要传入的参数内容'},
{type:0,content:'abc'},
{type:1,content:'参数内容abc'},
]"></chat-box>
## getNumArg()
@@ -389,14 +393,14 @@ public function argTest1() {
}
```
<chat-box>
) test abc 334 argtest
( 数字参数内容334
) test abc
( 请输入你要传入的数字内容
) 998
( 参数内容998
</chat-box>
<chat-box :my-chats="[
{type:0,content:'test abc 334 argtest'},
{type:1,content:'数字参数内容334'},
{type:0,content:'test abc'},
{type:1,content:'请输入你要传入的参数内容'},
{type:0,content:'998'},
{type:1,content:'参数内容998'},
]"></chat-box>
## copy()
@@ -418,8 +422,7 @@ public function argTest1() {
}
```
<chat-box>
) test abc 334 argtest
( 参数内容abc, 334, argtest
</chat-box>
<chat-box :my-chats="[
{type:0,content:'test abc 334 argtest'},
{type:1,content:'参数内容abc, 334, argtest'},
]"></chat-box>

View File

@@ -60,4 +60,3 @@ for($i = 0; $i < 1000; ++$i) {
`$name` 为字符串,是你要用的协程池的名称。
`$size` 为大小,最大不可超过 Swoole 配置文件中指定的最大协程数量。

View File

@@ -101,4 +101,4 @@ public function randNum() {
## EventDispatcher::enableEventTrace() - 启用事件跟踪器
还没写完,不着急。
还没写完,不着急。

View File

@@ -8,35 +8,35 @@
根据加载的用户编写的代码类名来获取类所在的文件路径。
=== "src/Module/Example/Hello.php"
**src/Module/Example/Hello.php**
```php
<?php
namespace Module\Example;
class Hello { ... }
```
```php
<?php
namespace Module\Example;
class Hello { ... }
```
=== "src/Module/Example/Start.php"
**src/Module/Example/Start.php**
```php
<?php
namespace Module\Example;
use ZM\Annotation\Swoole\OnStart;
class Start {
/**
* @OnStart()
*/
public function onStart() {
Console::info("Path: ".getClassPath(Hello::class));
}
}
```
```php
<?php
namespace Module\Example;
use ZM\Annotation\Swoole\OnStart;
class Start {
/**
* @OnStart()
*/
public function onStart() {
Console::info("Path: ".getClassPath(Hello::class));
}
}
```
=== "输出结果"
**输出结果**
```
[11:12:02] [I] [#0] Path: /mnt/d/project/zhamao-framework/src/Module/Example/Hello.ph
```
```
[11:12:02] [I] [#0] Path: /mnt/d/project/zhamao-framework/src/Module/Example/Hello.ph
```
## explodeMsg()
@@ -289,7 +289,7 @@ zm_dump($pass);
定义:`zm_config($name, $key = null)`
有关 ZMConfig 模块的说明,见 [指南 - 基本配置](/guide/basic-config/)。
有关 ZMConfig 模块的说明,见 [指南 - 基本配置](/guide/basic-config)。
```php
zm_config("global"); //等同于 ZMConfig::get("global");
@@ -321,4 +321,3 @@ zm_config("global", "swoole"); //等同于 ZMConfig::get("global", "swoole");
## zm_verbose()
`Console::verbose($msg)`

View File

@@ -1,37 +1,44 @@
# 远程终端
框架在 2.3 版本时删除了本地终端(就是框架启动后可以在终端输入一些参数),因为框架的多进程模式会导致终端输入错乱,所以暂时取消掉了。
而远程终端应运而生,为的是弥补这一功能。与之前不同的是,远程终端使用 nc 连接,无需任何其他组件和客户端,而且功能更丰富,支持自定义命令。
## 启用
有两种开启方式:
- 永久开启:全局配置文件中找到 `remote_terminal``status`,改为 true启动框架即可。
- 临时开启:启动框架时加上参数 `--remote-terminal`。例如:`vendor/bin/start server --remote-terminal`
## 配置
在一般情况下,框架为了安全,直接按照默认配置,会监听 `127.0.0.1:20002` 端口,不可以远程访问,只能使用本机的 nc 连接,效果如下:
本地主机:
![img.png](https://static.zhamao.me/images/docs/3432551c08b34ca10aaf19f3f82aedeb.png)
从别的主机:
![img.png](https://static.zhamao.me/images/docs/6f35f2745d66c7e186da75b6f09248c2.png)
如果将 `host` 改为 `0.0.0.0` 或对应监听地址,即可指向性访问。
但是,如果你又想远程连接,又想保证安全,那么可以设置一个 token 参数,来保证连接时需要输入 token 才能使用远程终端。
假设我们的 token 是 `iAMTokEn`
![img.png](https://static.zhamao.me/images/docs/e502af4c0fd9359615548303cacb70dd.png
)
![img.png](https://static.zhamao.me/images/docs/e502af4c0fd9359615548303cacb70dd.png)
## 使用
默认情况下,使用 `nc` 命令即可。
```bash
nc <your-host> <your-port> -vvv
# nc 127.0.0.1 20002 -vvv
```
输入 help 即可查看内置的常用指令:
![img.png](https://static.zhamao.me/images/docs/7b74aa2b487c86482097ec7692c66e08.png
)
![img.png](https://static.zhamao.me/images/docs/7b74aa2b487c86482097ec7692c66e08.png)

View File

@@ -1,4 +1,4 @@
# 单例类SingletonTrait
# 单例类 - SingletonTrait
单例类,顾名思义,就是让用户声明的类拥有单例的特性,而这一组件引入的方式也最直接。它是一个 PHP 的 `trait`
@@ -40,4 +40,4 @@ Foo::getInstance()->test = 5;
var_dump(Foo::getInstance()->test);
```
只需要在类中使用:`use \ZM\Utils\SingletonTrait;` 一句话即可。
只需要在类中使用:`use \ZM\Utils\SingletonTrait;` 一句话即可。

View File

@@ -23,4 +23,3 @@
```php
TaskManager::runTask("heavy_task", 100, "param1", "param2");
```

View File

@@ -64,4 +64,3 @@ array:31 [
```
> 为什么不能重载所有文件?因为框架是多进程模型,而重载相当于只重新启动了一次 Worker 进程Manager 和 Master 进程未重启,所以被 Manager、Master 进程已经加载的 PHP 文件无法使用 reload 命令重新加载。详见 [进阶 - 进程间隔离](/advanced/multi-process/#_5)。

View File

@@ -6,9 +6,11 @@ HTTP 路由管理器用作管理炸毛框架内 `@RequestMapping` 和静态目
> 2.3.0 版本起可用。
!!! warning "注意"
::: warning 注意
因为炸毛框架的路由实现是不基于跨进程的共享内存的,所以每次使用这里面的工具函数都需要单独在所有 Worker 进程中执行一次,最好的办法就是在启动框架时执行(`@OnStart(-1)` 即可,代表此注解事件将在每个工作进程中都被执行一次)。
因为炸毛框架的路由实现是不基于跨进程的共享内存的,所以每次使用这里面的工具函数都需要单独在所有 Worker 进程中执行一次,最好的办法就是在启动框架时执行(`@OnStart(-1)` 即可,代表此注解事件将在每个工作进程中都被执行一次)。
:::
## 方法
@@ -51,4 +53,4 @@ public function onStart() {
定义:`\Symfony\Component\Routing\RouteCollection | null`
炸毛框架使用了 Symfony 框架的 route 组件,有关详情请查阅 [文档](https://symfony.com/doc/current/routing.html)。
炸毛框架使用了 Symfony 框架的 route 组件,有关详情请查阅 [文档](https://symfony.com/doc/current/routing.html)。

View File

@@ -4,10 +4,11 @@
命名空间:`use ZM\Requests\ZMRequest;`
!!! warning "注意"
::: warning 注意
在使用 Swoole 4.6.0 以下(不包含)的版本时,最好使用 Swoole 官方推荐的 Saber 或者 ZMRequest 这个轻量的 HTTP 请求客户端,不要使用 curl_exec因为在老版本的 Swoole 上对 curl 的协程 Hook 支持不是很完善。
在使用 Swoole 4.6.0 以下(不包含)的版本时,最好使用 Swoole 官方推荐的 Saber 或者 ZMRequest 这个轻量的 HTTP 请求客户端,不要使用 curl_exec因为在老版本的 Swoole 上对 curl 的协程 Hook 支持不是很完善。
:::
## ZMRequest::get()
@@ -262,11 +263,12 @@ $a->onClose(function($client){
返回值:`true|false`,当为 `true` 时代表握手成功,此时可以在回调里愉快地收发消息了。如果为 `false` 表明握手失败。
!!! warning "注意"
::: warning 注意
这里由于是协程转异步,所以不能确定 `upgrade()``onMessage()` 哪个先会被触发(一般情况下如果服务器不是立刻响应回包信息,总是会先返回 `upgrade()` 的结果。
这里由于是协程转异步,所以不能确定 `upgrade()``onMessage()` 哪个先会被触发(一般情况下如果服务器不是立刻响应回包信息,总是会先返回 `upgrade()` 的结果。
:::
## 设置参数
见:[Swoole - HTTP 客户端](http://wiki.swoole.com/#/coroutine_client/http_client?id=set)

View File

@@ -1,3 +0,0 @@
# 框架组件
这里列举了框架内的你可能会用到的常用组件。

View File

@@ -36,14 +36,16 @@ src/
- 含义:模块的描述。
??? note "点我查看编写实例"
::: tip 编写实例
```json
{
"name": "my-first-module",
"description": "这个是一个示例模块打包教程"
}
```
```json
{
"name": "my-first-module",
"description": "这个是一个示例模块打包教程"
}
```
:::
#### - version
@@ -52,14 +54,17 @@ src/
版本处理方式和 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"
}
```
::: tip 编写实例
```json
{
"name": "my-first-module",
"description": "这个是一个示例模块打包教程",
"version": "1.0.0"
}
```
:::
#### - depends
@@ -68,17 +73,20 @@ src/
此处用作模块的依赖检测,假设模块 `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": "*"
}
::: tip 编写实例
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"depends": {
"bar": "^1.0",
"bsr": "*"
}
```
}
```
:::
#### - light-cache-store
@@ -89,16 +97,19 @@ src/
我们假设在项目模块中使用到了 `group-status` 这一个 LightCache那么只需要写 `light-cache-store` 配置项,在模块打包时就会将持久化的数据也打包到 phar 模块包内。
??? note "点我查看编写实例:"
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"light-cache-store": [
"group-status"
]
}
```
::: tip 编写实例
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"light-cache-store": [
"group-status"
]
}
```
:::
#### - global-config-override
@@ -109,14 +120,17 @@ src/
如果是 false那么和不指定此参数效果是一样的无需用户修改 global.php。
??? note "点我查看编写实例:"
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"global-config-override": "请将 static_file_server 的 status 改为 true"
}
```
::: tip 编写实例
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"global-config-override": "请将 static_file_server 的 status 改为 true"
}
```
:::
#### - allow-hotload
@@ -125,17 +139,23 @@ src/
当此项为 true 时,可以将模块包直接放入 `zm_data/modules` 文件夹下,然后将 `global.php` 中的 `module_loader` 项中的 `enable_hotload` 改为 true启动框架即可加载。
??? note "点我查看编写实例:"
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"allow-hotload": true
}
```
::: tip 编写实例
!!! warning "注意"
如果使用允许热加载,那么模块包中的配置最好不要有 `global-config-override` 和 `light-cache-store`,以此来达到最正确的效果,一般热加载更适合 Library类型的模块。
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"allow-hotload": true
}
```
:::
::: warning 注意
如果使用允许热加载,那么模块包中的配置最好不要有 `global-config-override``light-cache-store`,以此来达到最正确的效果,一般热加载更适合 Library类型的模块。
:::
#### - zm-data-store
@@ -146,17 +166,20 @@ src/
我们假设要打包一个 `{zm_data 目录}/config/` 目录及其目录下的文件,和一个 `main.png` 文件,下方是实例。
??? note "点我查看编写实例:"
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"zm-data-store": [
"config/",
"main.png"
]
}
```
::: tip 编写实例
```json
{
"name": "foo",
"description": "这个是一个示例模块打包教程",
"zm-data-store": [
"config/",
"main.png"
]
}
```
:::
在打包时框架会自动添加这些文件到 phar 插件包内,到解包时,会自动将这些文件释放到对应框架的 `zm_data` 目录下。
@@ -221,7 +244,6 @@ $ ./zhamao module:pack foo
- crash/swoole_error.log
- 必要的框架热加载以及解包需要的配置信息
## 打包命令
```bash
@@ -240,4 +262,3 @@ $ ./zhamao module:pack foo
```
通过此命令可以查看模块相关的信息,如未打包但已配置的模块信息等。

View File

@@ -81,4 +81,4 @@ zm_data/modules/foo.phar
请输入修改模式y(使用vim修改)/e(自行使用其他编辑器修改后确认)/N(默认暂不修改)[y/e/N]
```
一般这种情况,根据第二条提示(第二条提示为打包时填入的 `global-config-override`)。如果输入 y则会自动执行命令 `vim config/global.php`,如果输入的是 e则会等待你手动修改完成文件最后按回车完成修改。默认情况直接回车的话会跳过此步骤如果模块要求了修改但跳过修改安装后可能会有功能缺失等问题。
一般这种情况,根据第二条提示(第二条提示为打包时填入的 `global-config-override`)。如果输入 y则会自动执行命令 `vim config/global.php`,如果输入的是 e则会等待你手动修改完成文件最后按回车完成修改。默认情况直接回车的话会跳过此步骤如果模块要求了修改但跳过修改安装后可能会有功能缺失等问题。

View File

@@ -16,10 +16,11 @@ $config['init_atomics'] = [
这时我们就成功初始化两个原子计数器,名字分别为 `foo``bar`
!!! warning "注意"
::: warning 注意
初始化的值必须是不小于 0 的 int32 值!
初始化的值必须是不小于 0 的 int32 值!
:::
## 使用
@@ -59,7 +60,8 @@ class Hello {
设置计数的数字:`ZMAtomic::get("bar")->set(77);`
!!! note "提示"
::: tip 提示
还有一些不常用的方法,可以看 Swoole 官方的文档,这里就不一一列举了。
还有一些不常用的方法,可以看 Swoole 官方的文档,这里就不一一列举了。
:::

View File

@@ -101,7 +101,8 @@ $r = file_get_contents(working_dir() . "/composer.json");
file_put_contents("/tmp/test.txt", "hello world");
```
!!! warning "注意"
::: warning 注意
在默认的情况里,框架的根目录均为可写可读的,在读写文件时务必要注意目录的位置和权限。使用 `working_dir()` 获取目录后面需要加 `/` 再追加自己的文件名或子目录名。
在默认的情况里,框架的根目录均为可写可读的,在读写文件时务必要注意目录的位置和权限。使用 `working_dir()` 获取目录后面需要加 `/` 再追加自己的文件名或子目录名。
:::

View File

@@ -81,14 +81,14 @@ public function storeAfterRemove() {
}
```
<chat-box>
) store
( OK
) storeAfterRemove
( 内容存在!
^ 等待 30 秒
( 内容不存在!
</chat-box>
<chat-box :my-chats="[
{type:0,content:'store'},
{type:1,content:'OK'},
{type:0,content:'storeAfterRemove'},
{type:1,content:'内容存在!'},
{type:2,content:'等待 30 秒'},
{type:1,content:'内容不存在!'},
]"></chat-box>
### LightCache::update()
@@ -218,14 +218,14 @@ public function getStore() {
}
```
<chat-box>
^ 我在 2021-01-05 15:21:00 发送这条消息
) getStore
( 2021-01-05 15:20:00
^ 这时我用 Ctrl+C 停止框架,过一会儿再启动
) getStore
( 存储时间2021-01-05 15:20:00
</chat-box>
<chat-box :my-chats="[
{type:2,content:'我在 2021-01-05 15:21:00 发送这条消息'},
{type:0,content:'getStore'},
{type:1,content:'2021-01-05 15:20:00'},
{type:2,content:'这时我用 Ctrl+C 停止框架,过一会儿再启动'},
{type:0,content:'getStore'},
{type:1,content:'存储时间2021-01-05 15:20:00'},
]"></chat-box>
### 数据加锁
@@ -250,10 +250,11 @@ public function test() {
在运行完测试后,通过 `LightCache::get("web_count")`,获取到的数你会发现不是 200000。怎么回事呢请自行翻阅多进程开发相关的书籍哦或者简单理解为有一些情况下进程 1 执行到了 `if-else` 语句,另一个进程也执行到了这里,两次在代码层面加的数是相同的,则虽然请求了两次,但是后执行 set 的那个进程又覆盖了前一个进程执行的值,导致最终结果加了 1 而不是 2
!!! note "提示"
::: tip 提示
同样的场景,使用 ZMAtomic 就不需要使用锁了。Atomic 是一句话:`add(1)` 立即加值的。而 LightCache 需要加锁的情况一般都是 `get->改值->set` 这样的代码。
同样的场景,使用 ZMAtomic 就不需要使用锁了。Atomic 是一句话:`add(1)` 立即加值的。而 LightCache 需要加锁的情况一般都是 `get->改值->set` 这样的代码。
:::
解决这一问题,就需要用到锁。这种情况下,我们首先考虑的是自旋锁,框架也因此内置了一个方便使用的自旋锁组件。详见下一章:自旋锁。
@@ -271,13 +272,13 @@ public function test() {
### WorkerCache 跨进程大缓存
WorkerCache 和 LightCache 几乎完全不同WorkerCache 存储的方式说白了就是 PHP 的静态变量,不过框架支持使用封装好的进程间通信进行跨进程读取。但由于需要设置一个存储变量的进程,所以配置文件必须先指定要将数据存到哪个 Worker/TaskWorker 进程中。关于框架内多进程的说明,请见 [进阶 - 多进程 Hack](/advanced/multi-process/)。
WorkerCache 和 LightCache 几乎完全不同WorkerCache 存储的方式说白了就是 PHP 的静态变量,不过框架支持使用封装好的进程间通信进行跨进程读取。但由于需要设置一个存储变量的进程,所以配置文件必须先指定要将数据存到哪个 Worker/TaskWorker 进程中。关于框架内多进程的说明,请见 [进阶 - 多进程 Hack](/advanced/multi-process)。
定义:`ZM\Store\WorkerCache`
#### 配置
见 [基本配置](/guide/basic-config/)。
见 [基本配置](/guide/basic-config)。
#### WorkerCache::get()
@@ -342,12 +343,11 @@ class Hello {
}
```
<chat-box>
) set_store hello world
( 成功!
) get_store hello
( world
) get_store foo
( 内容不存在!
</chat-box>
<chat-box :my-chats="[
{type:0,content:'set_store hello world'},
{type:1,content:'成功!'},
{type:0,content:'get_store hello'},
{type:1,content:'world'},
{type:0,content:'get_store foo'},
{type:1,content:'内容不存在!'},
]"></chat-box>

View File

@@ -1,8 +1,10 @@
# MySQL 数据库(旧版组件)
!!! warning "注意"
::: warning 注意
此 MySQL 组件为旧版 MySQL 查询器组件,为了统一和提升对未来独立组件的兼容性,现转变为使用 `doctrine/dbal``doctrine/orm` 库来实现查询器,请转到 [MySQL 查询器]()。
此 MySQL 组件为旧版 MySQL 查询器组件,为了统一和提升对未来独立组件的兼容性,现转变为使用 `doctrine/dbal``doctrine/orm` 库来实现查询器,请转到 [MySQL 查询器]()。
:::
## 配置
@@ -78,8 +80,6 @@ DB::table("admin")->where("name", "fake_admin")->count();
//SELECT count(*) FROM admin WHERE name = 'fake_admin'
```
## 直接执行 SQL
> 在查询器外执行的 SQL 语句都不会被缓存,都是一定会请求数据库的。
@@ -98,4 +98,4 @@ $r = DB::rawQuery("SELECT * FROM admin WHERE name = ?", ["fake_admin"]);
echo $r[0]["password"];
```
> 参数查询已经从根本上杜绝了 SQL 注入的问题。
> 参数查询已经从根本上杜绝了 SQL 注入的问题。

View File

@@ -0,0 +1,3 @@
# MySQLStatement
你好啊,这里是 Statement。TODO

View File

@@ -1,4 +1,27 @@
# 执行 SQL 语句
# 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 |
## 配置
炸毛框架的数据库组件支持原生 SQL、查询构造器去掉了复杂的对象模型关联同时默认为数据库连接池使开发变得简单。
数据库的配置位于 `config/global.php` 文件的 `mysql_config` 段,见 [全局配置](../../../../guide/basic-config#mysql_config)。
如果 `mysql_config.host` 字段为空,则不创建数据库连接池,填写后将创建,且默认保持长连接。
## 执行 SQL 语句
在一开始,无论你做什么数据库操作,均需要获取一个 `\ZM\MySQL\MySQLWrapper` 作为你的操作对象。
@@ -7,11 +30,13 @@
$wrapper = \ZM\MySQL\MySQLManager::getWrapper();
```
!!! tip "提示"
::: tip 提示
这部分内容部分直接取自 [DBAL - Data Retrieval And Manipulation](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.13/reference/data-retrieval-and-manipulation.html) 原文并直接翻译,如有实际不同请提交 Issue 反馈。
这部分内容部分直接取自 [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 语句
预处理查询很巧妙地解决了 SQL 注入问题,并且可以方便地绑定参数进行查询。
@@ -27,7 +52,7 @@ $stmt->bindValue(2, "jack");
$resultSet = $stmt->executeQuery();
```
其中 `$resultSet``Statement` 方法相似,此处的对象可能是 [数据库语句对象](../mysql-statement) 或 数据库结果对象(结果对象与语句对象的 `fetchXXX()` 部分一致)。
其中 `$resultSet``Statement` 方法相似,此处的对象可能是 [数据库语句对象](./mysql-statement) 或 数据库结果对象(结果对象与语句对象的 `fetchXXX()` 部分一致)。
这里也可以使用命名标签,使用标签可以给相同参数处使用同一个标签:
@@ -38,7 +63,7 @@ $stmt->bindValue("name", "jack");
$resultSet = $stmt->executeQuery();
```
## 执行常规语句
### 执行常规语句
执行常规语句为 `statement` 方式执行,此方法执行后只返回影响的行数,而不返回结果,适用于 `UPDATE` 等语句。
@@ -48,7 +73,7 @@ $count = $wrapper->executeStatement('UPDATE users SET username = ? WHERE id = ?'
echo $count; // 1
```
## 执行查询语句
### 执行查询语句
为给定的 SQL 创建一个准备好的语句并将参数传递给 executeQuery 方法,然后返回结果集。此方法为上述的「预处理查询语句」的简化版,可直接在第二个参数使用 array 插入绑定参数执行。
@@ -68,7 +93,7 @@ array(
*/
```
### fetchAllAssociative()
#### fetchAllAssociative()
执行查询并将所有结果返回一个数组中。
@@ -79,7 +104,7 @@ $resultSet = $wrapper->fetchAllAssociative('SELECT * FROM user WHERE username =
// 结果同 executeQuery()->fetchAllAssociative() 中 $user 的值。
```
### fetchAllKeyValue()
#### fetchAllKeyValue()
执行查询并将前两列分别作为键和值提取到关联数组中。
@@ -93,7 +118,7 @@ array(
*/
```
### fetchAllAssociativeIndexed()
#### fetchAllAssociativeIndexed()
执行查询并将数据作为关联数组获取,其中键代表第一列,值是其余列及其值的关联数组。
@@ -111,7 +136,7 @@ array(
*/
```
### fetchNumeric()
#### fetchNumeric()
查询并返回第一行数据,形式以数字索引方式返回每一列。
@@ -127,7 +152,7 @@ array(
*/
```
### fetchOne()
#### fetchOne()
仅返回查询结果的第一行第一列的值。
@@ -136,7 +161,7 @@ $username = $wrapper->fetchOne('SELECT username FROM users WHERE id = ?', array(
echo $username; // jack
```
### fetchAssociative()
#### fetchAssociative()
返回结果内第一行的关联数组形式的数据。
@@ -153,7 +178,7 @@ array(
*/
```
### delete()
#### delete()
删除查询操作,第一个参数为表名,第二个参数为 `['列名' => '列值']`
@@ -163,7 +188,7 @@ $wrapper->delete('users', array('username' => 'jack'));
// 等同于执行DELETE FROM user WHERE username = ? ,参数列表为('jack')
```
### insert()
#### insert()
插入数据库一行,第一个参数为表名,第二个参数为对应数据。
@@ -172,7 +197,7 @@ $wrapper->insert('users', array('id' => 0, 'username' => 'jwage', 'gender' => 'w
// INSERT INTO user (id, username, gender, update_time) VALUES (?,?,?,?) (0,jwage,woman,2021-10-17)
```
### update()
#### update()
更新数据库,使用给定数据更新匹配键值标识符的所有行。
@@ -181,4 +206,3 @@ $wrapper->insert('users', array('id' => 0, 'username' => 'jwage', 'gender' => 'w
$wrapper->update('user', array('username' => 'jwage'), array('id' => 1));
// UPDATE user (username) VALUES (?) WHERE id = ? (jwage, 1)
```

View File

@@ -1,7 +0,0 @@
# 配置
炸毛框架的数据库组件支持原生 SQL、查询构造器去掉了复杂的对象模型关联同时默认为数据库连接池使开发变得简单。
数据库的配置位于 `config/global.php` 文件的 `mysql_config` 段,见 [全局配置](../../../../guide/basic-config#mysql_config)。
如果 `mysql_config.host` 字段为空,则不创建数据库连接池,填写后将创建,且默认保持长连接。

View File

@@ -1 +0,0 @@
你好啊,这里是 Statement。

View File

@@ -1,14 +0,0 @@
# 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 |
#

View File

@@ -59,4 +59,4 @@ ZMRedis::call(function($redis) {
选一个喜欢的就好。硬要是说区别的话,对象模式是在 PHP 自动回收这个 `ZMRedis` 对象时会归还连接,也可以通过手动 `unset($obj)` 进行回收,否则就会执行到函数结尾自动回收。切记不可将 `$obj` 对象持久化存到静态或全局变量等。
回调模式看似是回调,但是是同步执行的,不会发生顺序错乱。也就是说到了 `ZMRedis::call()` 方法里面的时候,后面的代码不会提前执行,是顺序执行的。回调的作用仅仅是用作自动回收连接对象。
回调模式看似是回调,但是是同步执行的,不会发生顺序错乱。也就是说到了 `ZMRedis::call()` 方法里面的时候,后面的代码不会提前执行,是顺序执行的。回调的作用仅仅是用作自动回收连接对象。

View File

@@ -2,9 +2,11 @@
前面讲到 LightCache 轻量缓存在特定的情况下为了保证数据不被多进程的因素导致丢失或覆盖,在高并发情况下修改数据需要加锁,所以炸毛框架内置了 SpinLock 自旋锁。
!!! tip "提示"
::: tip 提示
框架单进程运行的模式下不需要任何自旋锁。
框架单进程运行的模式下不需要任何自旋锁。
:::
## 配置
@@ -61,9 +63,11 @@ public function test() {
原理剖析:在 LightCache 获取前,先对此内容上锁,这时如果其他进程有同时也在执行这个代码的时候,就会在 `SpinLock::lock()` 这行代码处原地等待,防止继续执行。等前面的那个进程执行到 `SpinLock::unlock()` 释放锁时,其他进程才可继续执行,从而避免了多个进程并行执行这段代码导致的数据错乱。
!!! error "警告"
::: danger 警告
使用锁时务必谨慎,如果不按照下面的规则使用自旋锁可能导致 CPU 占用率上升。
使用锁时务必谨慎,如果不按照下面的规则使用自旋锁可能导致 CPU 占用率上升。
:::
自旋锁使用约定: