mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-03-17 20:54:52 +08:00
update docs
This commit is contained in:
parent
44979c670f
commit
6ba209c4c7
@ -53,4 +53,18 @@ class Hello {
|
||||
|
||||
机器人开发过程中常见的 `@CQCommand`,或者是 HTTP 服务器路由绑定 `@RequestMapping` 都是相当于由对应注解代表了事件,而 `@Middleware`,`@Closed` 等这类注解显然不代表任何事件,只能当作这个函数或类的修饰属性而已。代表了事件的注解,我们称之为**注解事件**,它会在某种事件达成条件后触发注解下方的函数本身。
|
||||
|
||||
值得注意的是,注解事件本身概念是我凭空捏造的,我不好解释所以只能创造这么一个词来代指这一抽象的概念,硬要解释的话,大致就好比一个社区里有一个卖牛奶的,有几家人订阅了每日上门送牛奶的服务,只要你打了“给我配送牛奶”的注解,他就会上门。而它送的不止一种奶,可以给你个性化定制,比如让卖牛奶的给你带包糖带瓶水,而描述这个的注解就只能做一个之前注解的修饰。假设你只写了带包糖的注解,没有写给我配送牛奶的注解,那他永远也不会给你送牛奶和糖过来。
|
||||
值得注意的是,注解事件本身概念是我凭空捏造的,我不好解释所以只能创造这么一个词来代指这一抽象的概念,硬要解释的话,大致就好比一个社区里有一个卖牛奶的,有几家人订阅了每日上门送牛奶的服务,只要你打了“给我配送牛奶”的注解,他就会上门。而它送的不止一种奶,可以给你个性化定制,比如让卖牛奶的给你带包糖带瓶水,而描述这个的注解就只能做一个之前注解的修饰。假设你只写了带包糖的注解,没有写给我配送牛奶的注解,那他永远也不会给你送牛奶和糖过来。
|
||||
|
||||
## 阻断事件
|
||||
|
||||
由于炸毛框架内的注解事件统一由一个通用的事件分发器进行分发,所以你在任何注解事件内都可以用通用的方式阻断当前正在运行的事件。
|
||||
|
||||
首先就是要记得先 use 事件分发器的类:`use ZM\Event\EventDispatcher;`。
|
||||
|
||||
```php
|
||||
EventDispatcher::interrupt();
|
||||
EventDispatcher::interrupt($data); // 也可以带返回值,自定义注解事件时有用。
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
119
docs/event/事件分发器.md
Normal file
119
docs/event/事件分发器.md
Normal file
@ -0,0 +1,119 @@
|
||||
# 事件分发器(进阶)
|
||||
|
||||
事件分发器是以上所有注解事件执行函数的一个分发器,如果你在上一章已经学会了如何创建自定义注解,那么本章就来说明如何用内置的事件分发器进行分发自定义事件。
|
||||
|
||||
如果你不需要了解或自定义有关事件分发的功能,此处可无需阅读。
|
||||
|
||||
## 属性
|
||||
|
||||
- 类名:`ZM\Event\EventDispatcher`
|
||||
|
||||
## 方法
|
||||
|
||||
### EventDispatcher::interrupt()
|
||||
|
||||
阻断当前正在运行的事件,只能在事件内部被调用的函数中实现。
|
||||
|
||||
### __construct()
|
||||
|
||||
构造方法。
|
||||
|
||||
```php
|
||||
EventDispatcher::__construct(string $class = '')
|
||||
```
|
||||
|
||||
初始化一个事件分发器,可进行一系列设置,对事件分发做限定。
|
||||
|
||||
#### 参数
|
||||
|
||||
`$class`:设置要分发的事件对应的注解类名,支持自定义注解(例如 `CQMessage::class`)
|
||||
|
||||
### setRuleFunction()
|
||||
|
||||
设置函数触发规则判定的函数(就是在执行事件函数前执行的规则判定)
|
||||
|
||||
```php
|
||||
setRuleFunction(callable $rule = null)
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
`$rule`:支持回调或闭包。闭包的参数为执行对应事件函数所绑定的注解事件对象。
|
||||
|
||||
```php
|
||||
$dispatcher = new EventDispatcher(CustomEvent::class);
|
||||
$dispatcher->setRuleFunction(function($obj) {
|
||||
return $obj->name == "zhamao" ? true : false;
|
||||
});
|
||||
```
|
||||
|
||||
上方的 `$obj` 就是 CustomEvent 类的实例,参数绑定为注解中对应的参数。
|
||||
|
||||
### setResultFunction()
|
||||
|
||||
设置事件函数返回值处理的回调函数。
|
||||
|
||||
```php
|
||||
setReturnFunction(callable $return_func)
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
`$return_func`:设置事件函数返回值处理的回调函数,回调参数绑定为对应单独事件函数的返回值。
|
||||
|
||||
```php
|
||||
$dispatcher = new EventDispatcher(CustomEvent::class);
|
||||
$dispatcher->setReturnFunction(function($return) {
|
||||
if (is_string($return)) Console::info("函数返回了 ".$return);
|
||||
});
|
||||
```
|
||||
|
||||
### dispatchEvents()
|
||||
|
||||
开始分发事件。
|
||||
|
||||
```php
|
||||
dispatchEvents(...$params)
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
自定义参数,这里填入的参数将被填入被分发的函数参数中。
|
||||
|
||||
```php
|
||||
$dispatcher->dispatchEvents("foo", "bar");
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
class Test {
|
||||
/**
|
||||
* @CustomEvent("zhamao")
|
||||
*/
|
||||
public function test($arg1, $arg2) {
|
||||
echo "$arg1: $arg2"; //将输出 "foo: bar"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 机制
|
||||
|
||||
事件分发器的机制说简单不简单,说复杂也不复杂,它和中间件有着非常大的关系,因为它会自动检测和识别所要执行的函数有没有中间件,并且根据顺序进行执行。
|
||||
|
||||
在炸毛框架内部,一个完整的事件流程和中间件的关系如下图:
|
||||
|
||||

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

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

|
||||
|
||||
## 实战例子
|
||||
|
||||
我们假设 CustomEvent 是我们的自定义注解。还没写完,这部分太复杂了,而且举例子也不好举例,这块应该也不用着急更新。
|
||||
|
||||
TODO:待完成
|
||||
@ -1,3 +1,72 @@
|
||||
# 框架核心注解事件
|
||||
|
||||
TODO:师傅,莫催,快肝完了!
|
||||
框架核心注解事件区别于机器人和路由注解事件,这里框架注解事件都是**直接**或封装调用 Swoole 的回调事件的,所以对一些比较底层或者基础的操作都在这里做,例如收到 HTTP 或 WebSocket 连接后执行的事件函数。
|
||||
|
||||
## OnSwooleEvent()
|
||||
|
||||
绑定 Swoole 所相关的事件,例如 WebSocket 接入、收到 WS 消息、关闭 WS 连接,HTTP 请求到达等。
|
||||
|
||||
### 属性
|
||||
|
||||
| 类型 | 值 |
|
||||
| ------------ | ------------------------------------------ |
|
||||
| 名称 | `@OnSwooleEvent` |
|
||||
| 触发前提 | 当参数指定的 `type` 对应的事件被触发后激活 |
|
||||
| 命名空间 | `ZM\Annotation\Swoole\OnSwooleEvent` |
|
||||
| 适用位置 | 方法 |
|
||||
| 返回值处理 | 无 |
|
||||
| 注解绑定参数 | |
|
||||
|
||||
### 注解参数
|
||||
|
||||
| 参数名称 | 参数范围 | 用途 | 默认 |
|
||||
| -------- | -------------------------------------------------------- | ----------------------------------------------- | ---------------- |
|
||||
| type | `string`,支持填入 `open`,`request`,`close`,`message` | 限定事件的类型,**必填** | |
|
||||
| rule | `string`,必须是可执行且返回 bool 的 PHP 代码 | 例如判断连接是否为 QQ 机器人(`connectIsQQ()`) | 空,rule 为 true |
|
||||
| level | `int` | 事件优先级(越大越靠前) | 20 |
|
||||
|
||||
### 事件绑定参数
|
||||
|
||||
`$conn`: [ConnectionObject](/advanced/inside-class/) 类型,返回一个当前 WS 连接的连接对象。
|
||||
|
||||
### 示例1(机器人连接框架后输出信息)
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\ConnectionManager\ConnectionObject;
|
||||
use ZM\Console\Console;
|
||||
class Hello {
|
||||
/**
|
||||
* 在机器人客户端连接框架后向终端输出信息
|
||||
* @OnSwooleEvent("open",rule="connectIsQQ()")
|
||||
* @param $conn
|
||||
*/
|
||||
public function onConnect(ConnectionObject $conn) {
|
||||
Console::info("机器人 " . $conn->getOption("connect_id") . " 已连接!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这里的 Console 是终端输出组件,详情见组件一栏对应的文档查询。
|
||||
|
||||
### 示例2(阻断 Chrome 访问框架时多访问一次的问题)
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Module\Example;
|
||||
use ZM\Annotation\Swoole\OnSwooleEvent;
|
||||
use ZM\Event\EventDispatcher;
|
||||
class Hello {
|
||||
/**
|
||||
* 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
|
||||
* @OnSwooleEvent("request",rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
|
||||
*/
|
||||
public function onRequest() {
|
||||
EventDispatcher::interrupt();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 EventDispatcher 为事件分发器,interrupt 是通用阻断方法,如果你平常只使用阻断,则只需掌握这一个方法即可,`EventDispatcher::interrupt()` 在所有事件内可用。
|
||||
@ -122,4 +122,110 @@ public function index($arg) {
|
||||
|
||||
## 获取请求参数 GET / POST
|
||||
|
||||
炸毛框架支持获取外部 HTTP 请求进来的 GET 和 POST 请求,通过获取 HTTP 请求对象 [`Request`](/advanced/inside-class/) 即可。
|
||||
炸毛框架支持获取外部 HTTP 请求进来的 GET 和 POST 请求,通过获取 HTTP 请求对象 [Request](/advanced/inside-class/) 即可。对象具体属性和方法点这个链接进去就行。
|
||||
|
||||
### 示例
|
||||
|
||||
=== "获取 GET"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/testUrl")
|
||||
*/
|
||||
public function testUrl() {
|
||||
$get = ctx()->getRequest()->get;
|
||||
if(isset($get["name"])) return "hello, ".$get["name"];
|
||||
else return "Unknown name!!";
|
||||
}
|
||||
```
|
||||
|
||||
=== "获取 POST(x-www-form-urlencoded)"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/testUrl")
|
||||
*/
|
||||
public function testUrl() {
|
||||
$post = ctx()->getRequest()->post;
|
||||
if(isset($post["name"])) return "hello, ".$post["name"];
|
||||
else return "Unknown name!!";
|
||||
}
|
||||
```
|
||||
|
||||
=== "获取 JSON POST"
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/testUrl")
|
||||
*/
|
||||
public function testUrl() {
|
||||
$post = ctx()->getRequest()->rawContent();
|
||||
$json = json_decode($post, true);
|
||||
if ($json === null) return "Invalid json data!";
|
||||
if(isset($json["name"])) return "hello, ".$json["name"];
|
||||
else return "Unknown name!!";
|
||||
}
|
||||
```
|
||||
|
||||
## 设置路由请求方式
|
||||
|
||||
如果想要设置允许请求控制器的 HTTP 请求方式,可以使用方法在控制器中的 `@RequestMapping` 注解配置 `method` 参数,可以是 `GET`,`POST`,`PUT`, `PATCH`,`DELETE`,`OPTIONS`,`HEAD` 中的一个或多个。
|
||||
|
||||
- 限定 HTTP 方法:`@RequestMapping(method="GET")`,`@RequestMapping(method={"GET","POST"})`
|
||||
|
||||
## 静态文件服务器
|
||||
|
||||
框架支持了静态文件的访问。如需使用,则需要先到配置文件中配置相应的 `static_file_server` 参数中 `status` 为 `true`。
|
||||
|
||||
框架分为两种静态文件服务器,一种是全局的静态文件服务器,比如框架部署在 `http://127.0.0.1:20001/` 上通过 HTTP 访问,如果没有访问到 `@RequestMapping` 注解事件注册的路由地址,则会通过 url 自动查找静态文件服务器设置的根路径下面的文件,如果都不存在则会返回 404。
|
||||
|
||||
### 配置全局静态文件服务器
|
||||
|
||||
我们假设在你写的框架应用的根目录下,有如下文件和内容:
|
||||
|
||||
```
|
||||
resources/html/hello.html (下面是内容)
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
框架文档内容太多了,写不完!!!
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
然后在 `global.php` 配置文件中静态文件服务器参数为:
|
||||
|
||||
```php
|
||||
/** 静态文件访问 */
|
||||
$config['static_file_server'] = [
|
||||
'status' => true,
|
||||
'document_root' => realpath(__DIR__ . "/../") . '/resources/html',
|
||||
'document_index' => [
|
||||
'index.html'
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
最终,我们通过 `vendor/bin/start server` 等方式,启动框架后,浏览器访问 `http://127.0.0.1:20001/hello.html` 即可获取内容。
|
||||
|
||||
### 配置局部静态文件服务器
|
||||
|
||||
所涉及的类的命名空间:`use ZM\Http\StaticFileHandler;`
|
||||
|
||||
局部静态文件服务器一般用于,比如机器人要发送图片,或者给其他 HTTP 服务提供文件下载的接口时可用。我们假设写了一个图片收集的一个静态文件夹区域,将其中一个子路由当作图片静态目录:
|
||||
|
||||
```php
|
||||
/**
|
||||
* @RequestMapping("/images/{filename}")
|
||||
* @param $param
|
||||
* @return StaticFileHandler
|
||||
*/
|
||||
public function staticImage($param) {
|
||||
Console::info("[下载图片] " . $param["filename"]);
|
||||
return new StaticFileHandler($param["filename"], "/path/to/your/image_dir/");
|
||||
}
|
||||
```
|
||||
|
||||
这样当用户访问 `http://框架地址/images/aaa.jpg` 就可以快速地调用此路由下的局部文件服务器功能了。
|
||||
@ -66,6 +66,7 @@ nav:
|
||||
- 框架核心注解事件: event/框架注解事件.md
|
||||
- 中间件注解: event/中间件.md
|
||||
- 自定义注解: event/自定义注解.md
|
||||
- 事件分发器: event/事件分发器.md
|
||||
- 框架组件:
|
||||
- 框架组件: component/index.md
|
||||
- 进阶开发:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user