mirror of
https://gitee.com/mix-php/mix.git
synced 2024-12-02 03:37:56 +08:00
feat:docs:up 3.0
This commit is contained in:
parent
563c2963d5
commit
0aa75496af
2
docs/3.0/_navbar.md
Normal file
2
docs/3.0/_navbar.md
Normal file
@ -0,0 +1,2 @@
|
||||
* 多语言/Translations
|
||||
* [简体中文](zh-cn/)
|
@ -61,7 +61,7 @@
|
||||
'https://github.com/mix-php/mix/blob/master/docs/' +
|
||||
vm.route.file;
|
||||
}
|
||||
var editHtml = '<p style="text-align: right"><a href="https://github.com/mix-php/mix/blob/master/docs/3.0/zh-cn/README.md" target="_blank" rel="noopener"><img class="emoji" src="https://github.githubassets.com/images/icons/emoji/memo.png" alt="memo"> Edit Document</a></p>\n\n';
|
||||
var editHtml = '<p style="text-align: right"><a href="https://github.com/mix-php/mix/blob/master/docs/3.0/zh-cn/README.md" target="_blank" rel="noopener"><img class="emoji" src="https://github.githubassets.com/images/icons/emoji/memo.png" alt="memo"> Edit on github</a></p>\n\n';
|
||||
return (
|
||||
editHtml +
|
||||
html
|
||||
@ -73,5 +73,6 @@
|
||||
</script>
|
||||
<!-- Docsify v4 -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
<h1 align="center">Mix PHP</h1>
|
||||
|
||||
中文 | [English](README_EN.md)
|
||||
## 简介
|
||||
|
||||
MixPHP 是一个 PHP 命令行模式开发框架;基于 `Vega` 驱动的 HTTP 可以同时支持 Swoole、WorkerMan、FPM、CLI-Server 生态,并且可以无缝切换;`V3` 是一个高度解耦的版本,整体代码基于多个独立的模块构建,即便用户不使用我们的脚手架,也可以使用这些独立模块,并且全部模块都支持原生开发。例如:你可以只使用 mix/vega 来搭配 laravel orm 使用;可以在任意环境中使用 mix/database 和 mix/redis;可以使用 mix/grpc 原生代码编写 gRPC;所有的模块你可以像搭积木一样随意组合。
|
||||
|
||||
@ -18,79 +18,28 @@ MixPHP 是一个 PHP 命令行模式开发框架;基于 `Vega` 驱动的 HTTP
|
||||
|
||||
核心模块全部可独立使用,并且都支持原生代码开发。
|
||||
|
||||
- [mix/vega](src/vega) PHP 编写的 CLI 模式 HTTP 网络框架,支持 Swoole、WorkerMan、FPM、CLI-Server
|
||||
- [mix/database](src/database) 可在各种环境中使用的轻量数据库,支持 FPM、CLI、Swoole、WorkerMan,可选的连接池 (协程)
|
||||
- [mix/redis](src/redis) 可在各种环境中使用的 PHP Redis,支持 FPM、CLI、Swoole、WorkerMan,可选的连接池 (协程)
|
||||
- [mix/redis-subscriber](src/redis-subscriber) 基于 Swoole 协程的 Redis 原生协议订阅库
|
||||
- [mix/grpc](src/grpc) 基于 Swoole 协程的 PHP gRPC 库,包含 protoc 代码生成器、服务器、客户端
|
||||
- [mix/websocket](src/websocket) 基于 Swoole 协程的 PHP WebSocket 服务器与客户端
|
||||
- [mix/cli](src/cli) PHP 命令行交互指挥官
|
||||
- [mix/worker-pool](src/worker-pool) 基于 Swoole 的协程池、工作池库
|
||||
- [mix/validator](src/validator) 基于 PSR-7 的验证库
|
||||
- [mix/event](src/event) 基于 PSR-14 标准的事件调度库
|
||||
- [mix/vega](zh-cn/mix-vega) PHP 编写的 CLI 模式 HTTP 网络框架,支持 Swoole、WorkerMan、FPM、CLI-Server
|
||||
- [mix/database](zh-cn/mix-database) 可在各种环境中使用的轻量数据库,支持 FPM、CLI、Swoole、WorkerMan,可选的连接池 (协程)
|
||||
- [mix/redis](zh-cn/mix-redis) 可在各种环境中使用的 PHP Redis,支持 FPM、CLI、Swoole、WorkerMan,可选的连接池 (协程)
|
||||
- [mix/redis-subscriber](zh-cn/mix-redis-subscriber) 基于 Swoole 协程的 Redis 原生协议订阅库
|
||||
- [mix/grpc](zh-cn/mix-grpc) 基于 Swoole 协程的 PHP gRPC 库,包含 protoc 代码生成器、服务器、客户端
|
||||
- [mix/websocket](zh-cn/mix-websocket) 基于 Swoole 协程的 PHP WebSocket 服务器与客户端
|
||||
- [mix/cli](zh-cn/mix-cli) PHP 命令行交互指挥官
|
||||
- [mix/worker-pool](zh-cn/mix-worker-pool) 基于 Swoole 的协程池、工作池库
|
||||
- [mix/validator](zh-cn/mix-validator) 基于 PSR-7 的验证库
|
||||
- [mix/event](zh-cn/mix-event) 基于 PSR-14 标准的事件调度库
|
||||
|
||||
## 服务器
|
||||
|
||||
支持多种服务器驱动,并且可以无缝切换。
|
||||
|
||||
- [PHP Built-in CLI-Server](examples/api-skeleton/composer.json#L8) `零扩展依赖` `热更新` `适合本机开发`
|
||||
- [PHP-FPM](examples/api-skeleton/public/index.php) `热更新` `适合共享开发` `适合 admin 开发`
|
||||
- [Swoole](examples/api-skeleton/composer.json#L9) `常驻内存` `兼容 composer 生态`
|
||||
- [Swoole Coroutine](examples/api-skeleton/composer.json#L10) `常驻内存` `协程性能强劲`
|
||||
- [WorkerMan](examples/api-skeleton/composer.json#L11) `常驻内存` `兼容 composer 生态`
|
||||
- [PHP Built-in CLI-Server](zh-cn/server-cli-server.md) `零扩展依赖` `热更新` `适合本机开发`
|
||||
- [PHP-FPM](zh-cn/server-php-fpm.md) `热更新` `适合共享开发` `适合 admin 开发`
|
||||
- [Swoole](zh-cn/server-swoole.md) `常驻内存` `兼容 composer 生态`
|
||||
- [Swoole Coroutine](zh-cn/server-swoole-coroutine.md) `常驻内存` `协程性能强劲`
|
||||
- [WorkerMan](zh-cn/server-workerman.md) `常驻内存` `兼容 composer 生态`
|
||||
|
||||
## 快速开始
|
||||
|
||||
提供了现成的脚手架,快速创建项目,立即产出。
|
||||
|
||||
- [编写一个 CLI 程序](examples/cli-skeleton#readme)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/cli-skeleton cli
|
||||
```
|
||||
|
||||
- [编写一个 API 接口](examples/api-skeleton#readme)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/api-skeleton api
|
||||
```
|
||||
|
||||
- [编写一个 Web 页面](examples/web-skeleton#readme)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/web-skeleton web
|
||||
```
|
||||
|
||||
- [编写一个 WebSocket 服务](examples/websocket-skeleton#readme)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/websocket-skeleton websocket
|
||||
```
|
||||
|
||||
- [编写一个 gRPC 接口](examples/grpc-skeleton#readme)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/grpc-skeleton grpc
|
||||
```
|
||||
|
||||
## 性能测试
|
||||
|
||||
- [Web Frameworks Benchmark](https://web-frameworks-benchmark.netlify.app/result?l=php)
|
||||
|
||||
![web-frameworks-benchmark.png](web-frameworks-benchmark.png)
|
||||
|
||||
## 推荐阅读
|
||||
|
||||
- [MixPHP V3 开发流程体验 Swoole, Workerman, FPM, CLI-Server 多种运行模式介绍](https://zhuanlan.zhihu.com/p/398381870)
|
||||
- [MixPHP V3 增加了 PHP-FPM、CLI-Server 的支持](https://zhuanlan.zhihu.com/p/394059925)
|
||||
- [MixPHP V3 发布前的感想, 有哪些变化和特点](https://zhuanlan.zhihu.com/p/392558932)
|
||||
|
||||
## 技术交流
|
||||
|
||||
知乎:https://www.zhihu.com/people/onanying
|
||||
官方QQ群:[284806582](https://shang.qq.com/wpa/qunwpa?idkey=b3a8618d3977cda4fed2363a666b081a31d89e3d31ab164497f53b72cf49968a), [825122875](http://shang.qq.com/wpa/qunwpa?idkey=d2908b0c7095fc7ec63a2391fa4b39a8c5cb16952f6cfc3f2ce4c9726edeaf20) 敲门暗号:phper
|
||||
|
||||
## Golang 框架
|
||||
## MixGo 框架
|
||||
|
||||
OpenMix 同时还有 Golang 生态的框架
|
||||
|
||||
@ -102,7 +51,3 @@ OpenMix 同时还有 Golang 生态的框架
|
||||
- `V2.1` https://www.kancloud.cn/onanying/mixphp2-1/content
|
||||
- `V2.0` https://www.kancloud.cn/onanying/mixphp2/content
|
||||
- `V1.*` https://www.kancloud.cn/onanying/mixphp1/content
|
||||
|
||||
## License
|
||||
|
||||
Apache License Version 2.0, http://www.apache.org/licenses/
|
||||
|
2
docs/3.0/zh-cn/_navbar.md
Normal file
2
docs/3.0/zh-cn/_navbar.md
Normal file
@ -0,0 +1,2 @@
|
||||
* 多语言/Translations
|
||||
* [简体中文](zh-cn/)
|
5
docs/3.0/zh-cn/benchmarks.md
Normal file
5
docs/3.0/zh-cn/benchmarks.md
Normal file
@ -0,0 +1,5 @@
|
||||
## Web Frameworks Benchmark
|
||||
|
||||
- [Web Frameworks Benchmark](https://web-frameworks-benchmark.netlify.app/result?l=php)
|
||||
|
||||
<img width="60%" src="https://gitee.com/mix-php/mix/raw/master/web-frameworks-benchmark.png" data-origin="https://github.com/mix-php/mix/raw/master/web-frameworks-benchmark.png" alt="web-frameworks-benchmark.png">
|
211
docs/3.0/zh-cn/mix-cli.md
Normal file
211
docs/3.0/zh-cn/mix-cli.md
Normal file
@ -0,0 +1,211 @@
|
||||
## Mix CLI
|
||||
|
||||
PHP 命令行交互指挥官
|
||||
|
||||
> go 版本:https://github.com/mix-go/xcli
|
||||
|
||||
## Overview
|
||||
|
||||
一个命令行交互与指挥管理工具,它可以让单个 CLI 可执行多种功能,同时它还包括命令行参数获取、全局异常捕获与处理等命令行开发常用功能。
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require mix/cli
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```php
|
||||
Mix\Cli\Cli::setName('app')->setVersion('0.0.0-alpha');
|
||||
$cmd = new Mix\Cli\Command([
|
||||
'name' => 'hello',
|
||||
'short' => 'Echo demo',
|
||||
'run' => function () {
|
||||
$name = Mix\Cli\Flag::match('n', 'name')->string('default');
|
||||
// do something
|
||||
}
|
||||
]);
|
||||
$opt = new Mix\Cli\Option([
|
||||
'names' => ['n', 'name'],
|
||||
'usage' => 'Your name'
|
||||
]);
|
||||
$cmd->addOption($opt);
|
||||
Mix\Cli\Cli::addCommand($cmd)->run();
|
||||
```
|
||||
|
||||
上面是采用闭包,也可以使用对象
|
||||
|
||||
```php
|
||||
class FooCommand implements Mix\Cli\RunInterface
|
||||
{
|
||||
public function main(): void
|
||||
{
|
||||
// do something
|
||||
}
|
||||
}
|
||||
$cmd = new Mix\Cli\Command([
|
||||
'name' => 'hello',
|
||||
'short' => 'Echo demo',
|
||||
'run' => new FooCommand(),
|
||||
]);
|
||||
```
|
||||
|
||||
查看整个命令行程序的帮助
|
||||
|
||||
```
|
||||
$ php app.php
|
||||
Usage: app.php [OPTIONS] COMMAND [ARG...]
|
||||
|
||||
Commands:
|
||||
hello Echo demo
|
||||
|
||||
Global Options:
|
||||
-h, --help Print usage
|
||||
-v, --version Print version information
|
||||
|
||||
Run 'app.php COMMAND --help' for more information on a command.
|
||||
|
||||
Developed with Mix PHP framework. (openmix.org/mix-php)
|
||||
```
|
||||
|
||||
查看命令行程序的版本信息
|
||||
|
||||
```
|
||||
$ php app.php -v
|
||||
app 0.0.0-alpha
|
||||
```
|
||||
|
||||
查看 `hello` 命令的帮助
|
||||
|
||||
```
|
||||
$ php app.php hello --help
|
||||
Usage: app.php hello [ARG...]
|
||||
|
||||
Command Options:
|
||||
-n, --name Your name
|
||||
|
||||
Developed with Mix PHP framework. (openmix.org/mix-php)
|
||||
```
|
||||
|
||||
执行 `hello` 命令
|
||||
|
||||
```
|
||||
$ php app.php hello
|
||||
```
|
||||
|
||||
## Flag 参数获取
|
||||
|
||||
参数规则 (部分UNIX风格+GNU风格)
|
||||
|
||||
```
|
||||
php /examples/app.php home -d -rf --debug -v vvv --page 23 -s=test --name=john arg0
|
||||
```
|
||||
|
||||
- 命令:
|
||||
- 第一个参数,可以为空:`home`
|
||||
- 选项:
|
||||
- 短选项:一个中杠,如 `-d`、`-rf`
|
||||
- 长选项:二个中杠,如:`--debug`
|
||||
- 选项值:
|
||||
- 无值:`-d`、`-rf`、 `--debug`
|
||||
- 有值(空格):`-v vvv`、`--page 23`
|
||||
- 有值(等号):`-s=test`、`--name=john`
|
||||
- 参数:
|
||||
- 没有定义 `-` 的参数:`arg0`
|
||||
|
||||
获取选项,可以获取 `string`、`bool`、`int`、`float` 多种类型,也可以指定默认值。
|
||||
|
||||
```php
|
||||
$name = Mix\Cli\Flag::match('n', 'name')->string('Xiao Ming');
|
||||
```
|
||||
|
||||
获取第一个参数
|
||||
|
||||
```php
|
||||
$arg0 = Mix\Cli\Flag::arguments()->first()->string();
|
||||
```
|
||||
|
||||
获取全部参数
|
||||
|
||||
```php
|
||||
foreach (Mix\Cli\Flag::arguments()->values() as $k => $v) {
|
||||
// do something
|
||||
}
|
||||
```
|
||||
|
||||
## Handle panic 错误处理
|
||||
|
||||
使用中间件处理异常,也可以单独对某个命令配置中间件
|
||||
|
||||
```php
|
||||
$h = function ($next) {
|
||||
try {
|
||||
$next();
|
||||
} catch (\Throwable $ex){
|
||||
if ($ex instanceof Mix\Cli\Exception\NotFoundException) {
|
||||
throw $ex;
|
||||
}
|
||||
// handle panic
|
||||
}
|
||||
};
|
||||
$cmd = new Mix\Cli\Command([
|
||||
'name' => 'hello',
|
||||
'short' => 'Echo demo',
|
||||
'run' => function () {
|
||||
// do something
|
||||
}
|
||||
]);
|
||||
Mix\Cli\Cli::use($h)->addCommand($cmd)->run();
|
||||
```
|
||||
|
||||
## Application
|
||||
|
||||
我们在编写代码时,可能会要用到 App 中的一些信息。
|
||||
|
||||
```
|
||||
// 获取基础路径(入口文件所在目录路径)
|
||||
Mix\Cli\Cli::app()->basePath
|
||||
|
||||
// App名称
|
||||
Mix\Cli\Cli::app()->name
|
||||
|
||||
// App版本号
|
||||
Mix\Cli\Cli::app()->version
|
||||
|
||||
// 是否开启debug
|
||||
Mix\Cli\Cli::app()->debug
|
||||
```
|
||||
|
||||
## Singleton 单命令
|
||||
|
||||
当我们的 CLI 只有一个命令时,只需要配置一下 `Singleton`:
|
||||
|
||||
~~~php
|
||||
$cmd = new Mix\Cli\Command([
|
||||
'name' => 'hello',
|
||||
'short' => 'Echo demo',
|
||||
'run' => function () {
|
||||
// do something
|
||||
},
|
||||
'singleton' => true,
|
||||
]);
|
||||
~~~
|
||||
|
||||
命令的 Options 将会在 `-h/--help` 中打印
|
||||
|
||||
~~~
|
||||
$ php app.php
|
||||
Usage: app.php [OPTIONS] COMMAND [ARG...]
|
||||
|
||||
Command Options:
|
||||
-n, --name Your name
|
||||
|
||||
Global Options:
|
||||
-h, --help Print usage
|
||||
-v, --version Print version information
|
||||
|
||||
Run 'app.php COMMAND --help' for more information on a command.
|
||||
|
||||
Developed with Mix PHP framework. (openmix.org/mix-php)
|
||||
~~~
|
350
docs/3.0/zh-cn/mix-database.md
Normal file
350
docs/3.0/zh-cn/mix-database.md
Normal file
@ -0,0 +1,350 @@
|
||||
## Mix Database
|
||||
|
||||
可在各种环境中使用的轻量数据库,支持 FPM、CLI、Swoole、WorkerMan,可选的连接池 (协程)
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
composer require mix/database
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```php
|
||||
$db = new Mix\Database\Database('mysql:host=127.0.0.1;port=3306;charset=utf8;dbname=test', 'username', 'password');
|
||||
```
|
||||
|
||||
创建
|
||||
|
||||
```php
|
||||
$db->insert('users', [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
]);
|
||||
```
|
||||
|
||||
查询
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->first();
|
||||
```
|
||||
|
||||
更新
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->update('name', 'foo1');
|
||||
```
|
||||
|
||||
删除
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->delete();
|
||||
```
|
||||
|
||||
## 启动连接池 Pool
|
||||
|
||||
在 `Swoole` 协程环境中,启动连接池
|
||||
|
||||
```php
|
||||
$maxOpen = 50; // 最大开启连接数
|
||||
$maxIdle = 20; // 最大闲置连接数
|
||||
$maxLifetime = 3600; // 连接的最长生命周期
|
||||
$waitTimeout = 0.0; // 从池获取连接等待的时间, 0为一直等待
|
||||
$db->startPool($maxOpen, $maxIdle, $maxLifetime, $waitTimeout);
|
||||
Swoole\Runtime::enableCoroutine(); // 必须放到最后,防止触发协程调度导致异常
|
||||
```
|
||||
|
||||
连接池统计
|
||||
|
||||
```php
|
||||
$db->poolStats(); // array, fields: total, idle, active
|
||||
```
|
||||
|
||||
## 创建 Insert
|
||||
|
||||
创建
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
];
|
||||
$db->insert('users', $data);
|
||||
```
|
||||
|
||||
获取 InsertId
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
];
|
||||
$insertId = $db->insert('users', $data)->lastInsertId();
|
||||
```
|
||||
|
||||
替换创建
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
];
|
||||
$db->insert('users', $data, 'REPLACE INTO');
|
||||
```
|
||||
|
||||
批量创建
|
||||
|
||||
```php
|
||||
$data = [
|
||||
[
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
],
|
||||
[
|
||||
'name' => 'foo1',
|
||||
'balance' => 0,
|
||||
]
|
||||
];
|
||||
$db->batchInsert('users', $data);
|
||||
```
|
||||
|
||||
使用函数创建
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
'add_time' => new Mix\Database\Expr('CURRENT_TIMESTAMP()'),
|
||||
];
|
||||
$db->insert('users', $data);
|
||||
```
|
||||
|
||||
## 查询 Select
|
||||
|
||||
### 获取结果
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| get(): array | 获取多行 |
|
||||
| first(): array or object | 获取第一行 |
|
||||
| value(string $field): mixed | 获取第一行某个字段 |
|
||||
| statement(): \PDOStatement | 获取原始结果集 |
|
||||
|
||||
### Where
|
||||
|
||||
#### AND
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ? AND name = ?', 1, 'foo')->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->where('name = ?', 'foo')->get();
|
||||
```
|
||||
|
||||
#### OR
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ? OR id = ?', 1, 2)->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->or('id = ?', 2)->get();
|
||||
```
|
||||
|
||||
#### IN
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id IN (?)', [1, 2])->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id NOT IN (?)', [1, 2])->get();
|
||||
```
|
||||
|
||||
### Select
|
||||
|
||||
```php
|
||||
$db->table('users')->select('id, name')->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->select('id', 'name')->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->select('name AS n')->get();
|
||||
```
|
||||
|
||||
### Order
|
||||
|
||||
```php
|
||||
$db->table('users')->order('id', 'desc')->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->order('id', 'desc')->order('name', 'asc')->get();
|
||||
```
|
||||
|
||||
### Limit
|
||||
|
||||
```php
|
||||
$db->table('users')->limit(5)->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('users')->offset(10)->limit(5)->get();
|
||||
```
|
||||
|
||||
### Group & Having
|
||||
|
||||
```php
|
||||
$db->table('news')->select('uid, COUNT(*) AS total')->group('uid')->having('COUNT(*) > ?', 0)->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('news')->select('uid, COUNT(*) AS total')->group('uid')->having('COUNT(*) > ? AND COUNT(*) < ?', 0, 10)->get();
|
||||
```
|
||||
|
||||
### Join
|
||||
|
||||
```php
|
||||
$db->table('news AS n')->select('n.*, u.name')->join('users AS u', 'n.uid = u.id')->get();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->table('news AS n')->select('n.*, u.name')->leftJoin('users AS u', 'n.uid = u.id AND u.balance > ?', 10)->get();
|
||||
```
|
||||
|
||||
## 更新 Update
|
||||
|
||||
更新单个字段
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->update('name', 'foo1');
|
||||
```
|
||||
|
||||
获取影响行数
|
||||
|
||||
```php
|
||||
$rowsAffected = $db->table('users')->where('id = ?', 1)->update('name', 'foo1')->rowCount();
|
||||
```
|
||||
|
||||
更新多个字段
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'name' => 'foo1',
|
||||
'balance' => 100,
|
||||
];
|
||||
$db->table('users')->where('id = ?', 1)->updates($data);
|
||||
```
|
||||
|
||||
使用表达式更新
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->update('balance', new Mix\Database\Expr('balance + ?', 1));
|
||||
```
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'balance' => new Mix\Database\Expr('balance + ?', 1),
|
||||
];
|
||||
$db->table('users')->where('id = ?', 1)->updates($data);
|
||||
```
|
||||
|
||||
使用函数更新
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->update('add_time', new Mix\Database\Expr('CURRENT_TIMESTAMP()'));
|
||||
```
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'add_time' => new Mix\Database\Expr('CURRENT_TIMESTAMP()'),
|
||||
];
|
||||
$db->table('users')->where('id = ?', 1)->updates($data);
|
||||
```
|
||||
|
||||
## 删除 Delete
|
||||
|
||||
删除
|
||||
|
||||
```php
|
||||
$db->table('users')->where('id = ?', 1)->delete();
|
||||
```
|
||||
|
||||
获取影响行数
|
||||
|
||||
```php
|
||||
$rowsAffected = $db->table('users')->where('id = ?', 1)->delete()->rowCount();
|
||||
```
|
||||
|
||||
## 原生 Raw
|
||||
|
||||
```php
|
||||
$db->raw('SELECT * FROM users WHERE id = ?', 1)->first();
|
||||
```
|
||||
|
||||
```php
|
||||
$db->exec('DELETE FROM users WHERE id = ?', 1)->rowCount();
|
||||
```
|
||||
|
||||
## 事务 Transaction
|
||||
|
||||
手动事务
|
||||
|
||||
```php
|
||||
$tx = $db->beginTransaction();
|
||||
try {
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
];
|
||||
$tx->insert('users', $data);
|
||||
$tx->commit();
|
||||
} catch (\Throwable $ex) {
|
||||
$tx->rollback();
|
||||
throw $ex;
|
||||
}
|
||||
```
|
||||
|
||||
自动事务,执行异常自动回滚并抛出异常
|
||||
|
||||
```php
|
||||
$db->transaction(function (Mix\Database\Transaction $tx) {
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'balance' => 0,
|
||||
];
|
||||
$tx->insert('users', $data);
|
||||
});
|
||||
```
|
||||
|
||||
## 调试 Debug
|
||||
|
||||
```php
|
||||
$db->debug(function (Mix\Database\ConnectionInterface $conn) {
|
||||
var_dump($conn->queryLog()); // array, fields: time, sql, bindings
|
||||
})
|
||||
->table('users')
|
||||
->where('id = ?', 1)
|
||||
->get();
|
||||
```
|
||||
|
||||
## 日志 Logger
|
||||
|
||||
日志记录器,配置后可打印全部SQL信息
|
||||
|
||||
```php
|
||||
$db->setLogger($logger);
|
||||
```
|
||||
|
||||
`$logger` 需实现 `Mix\Database\LoggerInterface`
|
||||
|
||||
```php
|
||||
interface LoggerInterface
|
||||
{
|
||||
public function trace(float $time, string $sql, array $bindings, int $rowCount, ?\Throwable $exception): void;
|
||||
}
|
||||
```
|
65
docs/3.0/zh-cn/mix-event.md
Normal file
65
docs/3.0/zh-cn/mix-event.md
Normal file
@ -0,0 +1,65 @@
|
||||
## Mix Event
|
||||
|
||||
基于 PSR-14 标准的事件调度库
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
composer require mix/event
|
||||
```
|
||||
|
||||
## 定义一个事件
|
||||
|
||||
事件可以为任意类,我们以 SQL 执行事件调度来举例
|
||||
|
||||
```php
|
||||
class DatabaseEvent
|
||||
{
|
||||
public $time = 0;
|
||||
public $sql = '';
|
||||
public $bindings = [];
|
||||
}
|
||||
```
|
||||
|
||||
## 定义一个监听器
|
||||
|
||||
监听器是用户编写处理事件逻辑代码的地方,`events` 方法返回一个要监听的事件类的数组,当这些事件触发时,会调用 `process` 方法
|
||||
|
||||
```php
|
||||
class DatabaseListener implements Mix\Event\ListenerInterface
|
||||
{
|
||||
|
||||
public function events(): array
|
||||
{
|
||||
// 要监听的事件数组,可监听多个事件
|
||||
return [
|
||||
DatabaseEvent::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function process(object $event): void
|
||||
{
|
||||
// 事件触发后,会执行该方法
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 创建调度器
|
||||
|
||||
创建调度器,并传入监听器,可传入多个
|
||||
|
||||
```php
|
||||
$dispatcher = new Mix\Event\EventDispatcher(new DatabaseListener());
|
||||
```
|
||||
|
||||
## 触发事件
|
||||
|
||||
在事件产生的位置触发事件,当后面需要对该事件扩展业务逻辑时,只需在监听器中增加代码即可,达到不污染正常业务流程的目的
|
||||
|
||||
```php
|
||||
$event = new DatabaseEvent();
|
||||
$event->time = 10;
|
||||
$event->sql = 'select * from users';
|
||||
$dispatcher->dispatch($event);
|
||||
```
|
230
docs/3.0/zh-cn/mix-grpc.md
Normal file
230
docs/3.0/zh-cn/mix-grpc.md
Normal file
@ -0,0 +1,230 @@
|
||||
## Mix gRPC
|
||||
|
||||
基于 Swoole 协程的 PHP gRPC 库,包含 protoc 代码生成器、服务器、客户端
|
||||
|
||||
## 简介
|
||||
|
||||
由于 PHP-FPM 的特殊生命周期,导致 PHP 的 gRPC 官方代码生成器只能生成数据结构和客户端代码,无法像 golang/node.js/python 一样能同时生成服务器代码;传统方式如果要搭建 PHP gRPC 服务器只能借助
|
||||
nginx+h2+phpfpm 来搭建,这样就不需要 server 代码了,但是短生命周期又无法很好的支持服务注册,因为这些原因导致 PHP 在 gRPC 中一直都是充当 Client 的角色,Mix gRPC 提供了基于 Swoole
|
||||
的方案:
|
||||
|
||||
- 使用 [Swoole](https://github.com/swoole/swoole-src) 作为 gRPC Server
|
||||
- 使用 Golang 打造的 protoc-gen-mix 来生成 service 的 server/client 代码
|
||||
- 完全独立,可在任何 CLI 模式的 php 代码中执行,任何框架的 CLI 模式中执行 Laravel、ThinkPHP、MixPHP 等都可以
|
||||
- 同时为了降低门槛,我已经将 protoc、protoc-gen-mix 文件编译好了 win、linux、macOS 三个系统的二进制文件,直接下载即可
|
||||
|
||||
让 PHP 编写 gRPC 和 Golang 一样方便快捷,同时性能强劲
|
||||
|
||||
**推荐搭配以下数据库使用 (支持协程和连接池):**
|
||||
|
||||
- https://github.com/mix-php/database
|
||||
- https://github.com/mix-php/redis
|
||||
|
||||
## 安装
|
||||
|
||||
- Swoole >= 4.4.4: https://wiki.swoole.com/#/environment
|
||||
- 需要开启 `--enable-http2`
|
||||
|
||||
```
|
||||
composer require mix/grpc
|
||||
```
|
||||
|
||||
## 下载 protoc 与相关 plugin
|
||||
|
||||
- [protoc](https://github.com/protocolbuffers/protobuf) 是 protobuf 数据结构代码生成器,负责将 .proto 数据结构文件生成为对应语言的 class、struct
|
||||
供程序使用,
|
||||
- [protoc-gen-mix](https://github.com/mix-php/grpc/tree/master/protoc-gen-mix) 是 mix 开发的 protoc 插件,用来生成 service 的
|
||||
server/client 代码。
|
||||
|
||||
以上 2 个二进制文件,我都帮你们编译好了,包含多个常用 OS 类型,直接下载即可:
|
||||
|
||||
- [下载 protoc_mix_plugin](https://github.com/mix-php/grpc/releases/tag/binary-210714) `win/macos/linux`
|
||||
|
||||
下载完成后 linux、macOS 将二进制文件放入系统 `/usr/local/bin` 目录,win 放入 `C:\WINDOWS\system32`
|
||||
|
||||
## 通过 .proto 生成 PHP 代码
|
||||
|
||||
首先我们编写一个 proto 文件:
|
||||
|
||||
```
|
||||
syntax = "proto3";
|
||||
|
||||
package php.micro.grpc.greeter;
|
||||
|
||||
service Say {
|
||||
rpc Hello(Request) returns (Response) {}
|
||||
}
|
||||
|
||||
message Request {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message Response {
|
||||
string msg = 1;
|
||||
}
|
||||
```
|
||||
|
||||
然后使用 protoc 生成代码:
|
||||
|
||||
```
|
||||
protoc --php_out=. --mix_out=. greeter.proto
|
||||
```
|
||||
|
||||
执行命令后将在当前目录生成以下文件:
|
||||
|
||||
```
|
||||
|-- GPBMetadata
|
||||
| `-- Greeter.php
|
||||
|-- Php
|
||||
| `-- Micro
|
||||
| `-- Grpc
|
||||
| `-- Greeter
|
||||
| |-- Request.php
|
||||
| |-- Response.php
|
||||
| |-- SayClient.php
|
||||
| `-- SayInterface.php
|
||||
`-- greeter.proto
|
||||
```
|
||||
|
||||
其中 Request.php、Response.php 为 `--php_out` 生成,SayClient.php SayInterface.php 由 `--mix_out` 生成。
|
||||
|
||||
接下来我们将生成的文件加入到 composer autoload 中,我们修改 composer.json:
|
||||
|
||||
```
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"GPBMetadata\\": "protodir/GPBMetadata/",
|
||||
"Php\\": "protodir/Php/"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
修改后执行 `composer dump-aotoload` 使其生效。
|
||||
|
||||
## 编写一个 gRPC 服务
|
||||
|
||||
我们用原生 PHP 代码来编写一个 gRPC 服务器:
|
||||
|
||||
```php
|
||||
// 编写一个服务,实现 protoc-gen-mix 生成的接口
|
||||
class SayService implements Php\Micro\Grpc\Greeter\SayInterface
|
||||
{
|
||||
|
||||
public function Hello(Mix\Grpc\Context $context, Php\Micro\Grpc\Greeter\Request $request): Php\Micro\Grpc\Greeter\Response
|
||||
{
|
||||
$response = new Php\Micro\Grpc\Greeter\Response();
|
||||
$response->setMsg(sprintf('hello, %s', $request->getName()));
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$grpc = new Mix\Grpc\Server();
|
||||
$grpc->register(SayService::class); // or $grpc->register(new SayService());
|
||||
```
|
||||
|
||||
Swoole 多进程 (异步) 中使用
|
||||
|
||||
```php
|
||||
$http = new Swoole\Http\Server('0.0.0.0', 9595);
|
||||
$http->on('Request', $grpc->handler());
|
||||
$http->set([
|
||||
'worker_num' => 4,
|
||||
'open_http2_protocol' => true,
|
||||
'http_compression' => false,
|
||||
]);
|
||||
$http->start();
|
||||
```
|
||||
|
||||
开启多进程协程
|
||||
|
||||
```php
|
||||
$http->on('Request', $grpc->handler());
|
||||
$http->on('WorkerStart', function ($server, $workerId) {
|
||||
// 协程初始化
|
||||
// 比如:启动 mix/database mix/redis 的连接池
|
||||
});
|
||||
$http->set([
|
||||
'enable_coroutine' => true,
|
||||
'worker_num' => 4,
|
||||
'open_http2_protocol' => true,
|
||||
'http_compression' => false,
|
||||
]);
|
||||
```
|
||||
|
||||
Swoole 单进程 (协程) 中使用
|
||||
|
||||
```php
|
||||
Swoole\Coroutine\run(function () use ($grpc) {
|
||||
$server = new Swoole\Coroutine\Http\Server('0.0.0.0', 9595, false);
|
||||
$server->handle('/', $grpc->handler());
|
||||
$server->set([
|
||||
'open_http2_protocol' => true,
|
||||
'http_compression' => false,
|
||||
]);
|
||||
$server->start();
|
||||
});
|
||||
```
|
||||
|
||||
## 客户端调用一个 gRPC 服务
|
||||
|
||||
通过 IP 端口调用 gRPC 服务
|
||||
|
||||
```php
|
||||
Swoole\Coroutine\run(function () {
|
||||
$client = new Mix\Grpc\Client('127.0.0.1', 9595);
|
||||
$say = new Php\Micro\Grpc\Greeter\SayClient($client);
|
||||
$request = new Php\Micro\Grpc\Greeter\Request();
|
||||
$request->setName('xiaoming');
|
||||
$ctx = new Mix\Grpc\Context();
|
||||
$response = $say->Hello($ctx, $request);
|
||||
var_dump($response->getMsg());
|
||||
});
|
||||
```
|
||||
|
||||
设置 `header`
|
||||
|
||||
```php
|
||||
$ctx->withHeader('foo', 'bar');
|
||||
$response = $say->Hello($ctx, $request);
|
||||
```
|
||||
|
||||
设置 `timeout`
|
||||
|
||||
```php
|
||||
$ctx->withTimeout(5.0);
|
||||
$response = $say->Hello($ctx, $request);
|
||||
```
|
||||
|
||||
## FPM 如何调用 gRPC 服务
|
||||
|
||||
像我们传统 PHP FPM 模式中,我们作为客户端调用 gRPC 比 Mix gRPC 提供的客户端要复杂很多,但是我们也经常需要用到,比如在 thinkphp laravel 中调用 Mix gRPC 或者 Mix Go 编写的 gRPC
|
||||
服务,推荐阅读以下文章:
|
||||
|
||||
- [gRPC入坑记](https://www.cnblogs.com/52fhy/p/11110704.html#php%E7%9B%B8%E5%85%B3%E6%94%AF%E6%8C%81)
|
||||
- [PHP中使用gRPC客户端](https://bbs.huaweicloud.com/blogs/135609)
|
||||
|
||||
网上的文章都缺少重要的一环,就是:
|
||||
|
||||
```
|
||||
protoc --php_out=. greeter.proto
|
||||
```
|
||||
|
||||
命令执行时,只会生成数据结构的 class 文件,不会生成 grpc 服务的客户端 class 文件
|
||||
|
||||
```
|
||||
service Say {
|
||||
rpc Hello(Request) returns (Response) {}
|
||||
}
|
||||
```
|
||||
|
||||
以上服务没有被处理,没有生成出 `SayClient.php` ,需要修改编译命令
|
||||
|
||||
```
|
||||
protoc --php_out=. --grpc_out=. --plugin=protoc-gen-grpc=/path/grpc_php_plugin greeter.proto
|
||||
```
|
||||
|
||||
命令中指定了一个 `grpc_php_plugin` 文件是由 [grpc/grpc](https://github.com/grpc/grpc/tree/master/src/php) 提供的源码,官方没有像 `protoc`
|
||||
一样提供编译好的二进制可以下载,只能自己编译。然而这个库依赖的大量的子仓库,在国内几乎无法拉取成功,其次 win 的 cmake 编译很多人不会弄,导致大量的人无法编译出这个文件,因此我这里直接提供编译好的二进制供大家下载。
|
||||
|
||||
- [下载 protoc_grpc_plugin](https://github.com/mix-php/grpc/releases/tag/binary-210714) `win/macos/linux`
|
59
docs/3.0/zh-cn/mix-redis-subscriber.md
Normal file
59
docs/3.0/zh-cn/mix-redis-subscriber.md
Normal file
@ -0,0 +1,59 @@
|
||||
## Mix Redis Subscriber
|
||||
|
||||
基于 Swoole 协程的 Redis 原生协议订阅库
|
||||
|
||||
使用 Socket 直接连接 Redis 服务器,不依赖 phpredis 扩展,该订阅器有如下优点:
|
||||
|
||||
- 平滑修改:可随时增加、取消订阅通道,实现无缝切换通道的需求。
|
||||
- 跨协程安全关闭:可在任意时刻关闭订阅。
|
||||
- 通道获取消息:该库封装风格参考 golang 语言 [go-redis](https://github.com/go-redis/redis) 库封装,通过 channel 获取订阅的消息。
|
||||
|
||||
## 安装
|
||||
|
||||
- Swoole >= 4.4
|
||||
|
||||
```
|
||||
composer require mix/redis-subscriber
|
||||
```
|
||||
|
||||
## 订阅频道
|
||||
|
||||
- 连接、订阅失败会抛出异常
|
||||
|
||||
```php
|
||||
$sub = new \Mix\Redis\Subscriber\Subscriber('127.0.0.1', 6379, '', 5); // 连接失败将抛出异常
|
||||
$sub->subscribe('foo', 'bar'); // 订阅失败将抛出异常
|
||||
|
||||
$chan = $sub->channel();
|
||||
while (true) {
|
||||
$data = $chan->pop();
|
||||
if (empty($data)) { // 手动close与redis异常断开都会导致返回false
|
||||
if (!$sub->closed) {
|
||||
// redis异常断开处理
|
||||
var_dump('Redis connection is disconnected abnormally');
|
||||
}
|
||||
break;
|
||||
}
|
||||
var_dump($data);
|
||||
}
|
||||
```
|
||||
|
||||
接收到订阅消息:
|
||||
|
||||
```
|
||||
object(Mix\Redis\Subscriber\Message)#8 (2) {
|
||||
["channel"]=>
|
||||
string(2) "foo"
|
||||
["payload"]=>
|
||||
string(4) "test"
|
||||
}
|
||||
```
|
||||
|
||||
## 全部方法
|
||||
|
||||
| 方法 | 描述 |
|
||||
| --- | --- |
|
||||
| subscribe(string ...$channels) : void | 增加订阅 |
|
||||
| unsubscribe(string ...$channels) : void | 取消订阅 |
|
||||
| channel() : Swoole\Coroutine\Channel | 获取消息通道 |
|
||||
| close() : void | 关闭订阅 |
|
88
docs/3.0/zh-cn/mix-redis.md
Normal file
88
docs/3.0/zh-cn/mix-redis.md
Normal file
@ -0,0 +1,88 @@
|
||||
## Mix Redis
|
||||
|
||||
可在各种环境中使用的 PHP Redis,支持 FPM、CLI、Swoole、WorkerMan,可选的连接池 (协程)
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer require mix/redis
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```php
|
||||
$rds = new Mix\Redis\Redis('127.0.0.1', 6379, 'password', 0);
|
||||
|
||||
$rds->set('foo', 'bar');
|
||||
$value = $rds->get('foo');
|
||||
```
|
||||
|
||||
## Start Pool
|
||||
|
||||
在 `Swoole` 协程环境中,启动连接池
|
||||
|
||||
```php
|
||||
$maxOpen = 50; // 最大开启连接数
|
||||
$maxIdle = 20; // 最大闲置连接数
|
||||
$maxLifetime = 3600; // 连接的最长生命周期
|
||||
$waitTimeout = 0.0; // 从池获取连接等待的时间, 0为一直等待
|
||||
$rds->startPool($maxOpen, $maxIdle, $maxLifetime, $waitTimeout);
|
||||
Swoole\Runtime::enableCoroutine(); // 必须放到最后,防止触发协程调度导致异常
|
||||
```
|
||||
|
||||
连接池统计
|
||||
|
||||
```php
|
||||
$rds->poolStats(); // array, fields: total, idle, active
|
||||
```
|
||||
|
||||
## Transaction Multi & Pipeline
|
||||
|
||||
Multi
|
||||
|
||||
事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由exec命令原子性(atomic)地执行。
|
||||
|
||||
```php
|
||||
$tx = $rds->multi();
|
||||
$tx->set('foo', 'bar');
|
||||
$tx->set('foo1', 'bar1');
|
||||
$ret = $tx->exec();
|
||||
```
|
||||
|
||||
Pipeline
|
||||
|
||||
客户端将执行的命令写入到缓冲中,最后由exec命令一次性发送给redis执行返回。
|
||||
|
||||
```php
|
||||
$tx = $rds->pipeline();
|
||||
$tx->set('foo', 'bar');
|
||||
$tx->set('foo1', 'bar1');
|
||||
$ret = $tx->exec();
|
||||
```
|
||||
|
||||
## Transaction Watch
|
||||
|
||||
监听值的变化,如果执行时有变化则事务失败,无变化则事务成功。
|
||||
|
||||
```php
|
||||
$tx = $rds->watch('foo');
|
||||
$tx->incr('foo');
|
||||
$ret = $tx->exec();
|
||||
```
|
||||
|
||||
## Logger
|
||||
|
||||
日志记录器,配置后可打印全部SQL信息
|
||||
|
||||
```php
|
||||
$db->setLogger($logger);
|
||||
```
|
||||
|
||||
`$logger` 需实现 `Mix\Redis\LoggerInterface`
|
||||
|
||||
```php
|
||||
interface LoggerInterface
|
||||
{
|
||||
public function trace(float $time, string $cmd, array $args, ?\Throwable $exception): void;
|
||||
}
|
||||
```
|
186
docs/3.0/zh-cn/mix-validator.md
Normal file
186
docs/3.0/zh-cn/mix-validator.md
Normal file
@ -0,0 +1,186 @@
|
||||
## Mix Validator
|
||||
|
||||
基于 PSR-7 标准的验证器
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
composer require mix/validator
|
||||
```
|
||||
|
||||
## 创建表单
|
||||
|
||||
继承 `Mix\Validator\Validator` 并定义:
|
||||
|
||||
- `public $name` 字段名
|
||||
- `rules()` 验证规则
|
||||
- `scenarios()` 验证场景
|
||||
- `messages()` 错误消息
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Forms;
|
||||
|
||||
use Mix\Validator\Validator;
|
||||
|
||||
class UserForm extends Validator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $age;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['string', 'maxLength' => 25, 'filter' => ['trim']],
|
||||
'age' => ['integer', 'unsigned' => true, 'min' => 1, 'max' => 120],
|
||||
'email' => ['email'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function scenarios(): array
|
||||
{
|
||||
return [
|
||||
'create' => ['required' => ['name'], 'optional' => ['email', 'age']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'name.required' => '名称不能为空.',
|
||||
'name.maxLength' => '名称最多不能超过25个字符.',
|
||||
'age.integer' => '年龄必须是数字.',
|
||||
'age.unsigned' => '年龄不能为负数.',
|
||||
'age.min' => '年龄不能小于1.',
|
||||
'age.max' => '年龄不能大于120.',
|
||||
'email' => '邮箱格式错误.',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## 在控制器中验证
|
||||
|
||||
```php
|
||||
// 使用表单验证
|
||||
$form = new UserForm($request->getAttributes());
|
||||
if (!$form->scenario('create')->validate()) {
|
||||
$data = ['code' => 1, 'message' => $form->error()];
|
||||
$ctx->JSON(200, $data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 将表单对象直接传递到模型中保存数据
|
||||
(new UserModel())->add($form);
|
||||
```
|
||||
|
||||
- `$form->error() : string` 获取单条错误信息
|
||||
- `$form->errors() : array` 获取全部错误信息
|
||||
|
||||
## 验证规则
|
||||
|
||||
全部的验证类型与对应的验证选项如下
|
||||
|
||||
```php
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'a' => ['integer', 'unsigned' => true, 'min' => 1, 'max' => 1000000, 'length' => 10, 'minLength' => 3, 'maxLength' => 5],
|
||||
'b' => ['double', 'unsigned' => true, 'min' => 1, 'max' => 1000000, 'length' => 10, 'minLength' => 3, 'maxLength' => 5],
|
||||
'c' => ['alpha', 'length' => 10, 'minLength' => 3, 'maxLength' => 5],
|
||||
'd' => ['alphaNumeric', 'length' => 10, 'minLength' => 3, 'maxLength' => 5],
|
||||
'e' => ['string', 'length' => 10, 'minLength' => 3, 'maxLength' => 5, 'filter' => ['trim', 'strip_tags', 'htmlspecialchars']],
|
||||
'f' => ['email', 'length' => 10, 'minLength' => 3, 'maxLength' => 5],
|
||||
'g' => ['phone'],
|
||||
'h' => ['url', 'length' => 10, 'minLength' => 3, 'maxLength' => 5],
|
||||
'i' => ['in', 'range' => ['A', 'B'], 'strict' => true],
|
||||
'j' => ['date', 'format' => 'Y-m-d'],
|
||||
'k' => ['compare', 'compareAttribute' => 'a'],
|
||||
'l' => ['match', 'pattern' => '/^[\w]{1,30}$/'],
|
||||
'm' => ['call', 'callback' => [$this, 'check']],
|
||||
'n' => ['file', 'mimes' => ['audio/mp3', 'video/mp4'], 'maxSize' => 1024 * 1],
|
||||
'r' => ['image', 'mimes' => ['image/gif', 'image/jpeg', 'image/png'], 'maxSize' => 1024 * 1],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### call 验证类型
|
||||
|
||||
该类型为用户自定义验证规则,callback 内指定一个用户自定义的方法来验证
|
||||
|
||||
```php
|
||||
public function check($fieldValue): bool
|
||||
{
|
||||
// 验证代码
|
||||
// ...
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### file / image 验证类型
|
||||
|
||||
该类型用来验证文件,包含的两个验证选项如下:
|
||||
|
||||
- mimes:输入你想要限制的文件mime类型,[MIME参考手册](http://www.w3school.com.cn/media/media_mimeref.asp)
|
||||
- maxSize:允许的文件最大尺寸,单位 KB
|
||||
|
||||
验证成功后模型类会增加一个同名属性,该属性为 Psr\Http\Message\UploadedFileInterface 类的实例化对象,可直接调用 $this->[attributeName]->moveTo($targetPath) 移动到你需要存放的位置
|
||||
|
||||
## 静态调用
|
||||
|
||||
```php
|
||||
// 验证是否为字母与数字
|
||||
Mix\Validator\Validate::isAlphaNumeric($value);
|
||||
|
||||
// 验证是否为字母
|
||||
Mix\Validator\Validate::isAlpha($value);
|
||||
|
||||
// 验证是否为日期
|
||||
Mix\Validator\Validate::isDate($value, $format);
|
||||
|
||||
// 验证是否为浮点数
|
||||
Mix\Validator\Validate::isDouble($value);
|
||||
|
||||
// 验证是否为邮箱
|
||||
Mix\Validator\Validate::isEmail($value);
|
||||
|
||||
// 验证是否为整数
|
||||
Mix\Validator\Validate::isInteger($value);
|
||||
|
||||
// 验证是否在某个范围
|
||||
Mix\Validator\Validate::in($value, $range, $strict = false);
|
||||
|
||||
// 正则验证
|
||||
Mix\Validator\Validate::match($value, $pattern);
|
||||
|
||||
// 验证是否为手机
|
||||
Mix\Validator\Validate::isPhone($value);
|
||||
|
||||
// 验证是否为网址
|
||||
Mix\Validator\Validate::isUrl($value);
|
||||
```
|
454
docs/3.0/zh-cn/mix-vega.md
Normal file
454
docs/3.0/zh-cn/mix-vega.md
Normal file
@ -0,0 +1,454 @@
|
||||
## Mix Vega
|
||||
|
||||
Vega 是一个用 PHP 编写的 CLI 模式 HTTP 网络框架,支持 Swoole、WorkerMan、FPM、CLI-Server
|
||||
|
||||
## 简介
|
||||
|
||||
Vega 是 [MixPHP](https://github.com/mix-php/mix) `V3+` 内置的最核心的组件 (可独立使用),参考
|
||||
golang [gin](https://github.com/gin-gonic/gin) [mux](https://github.com/gorilla/mux) 开发,它包含 Web 应用处理的大量功能 (数据库处理除外)
|
||||
,包括:路由、渲染、参数获取、中间件、文件上传、静态文件处理等;具有 CLI 模式下强大的兼容性,同时支持 Swoole、WorkerMan、FPM、CLI-Server, 并且支持 Swoole 的多种进程模型与协程。
|
||||
|
||||
**推荐搭配以下数据库使用:**
|
||||
|
||||
- https://github.com/mix-php/database
|
||||
- https://github.com/mix-php/redis
|
||||
- https://github.com/top-think/think-orm
|
||||
- https://github.com/illuminate/database
|
||||
|
||||
**推荐文章:**
|
||||
|
||||
- [使用 mix/vega + mix/db 进行现代化的原生 PHP 开发](https://zhuanlan.zhihu.com/p/387493850)
|
||||
|
||||
## 安装
|
||||
|
||||
> 需先安装 [Swoole](https://wiki.swoole.com/#/environment) 或者 [WorkerMan](http://doc.workerman.net/install/requirement.html)
|
||||
|
||||
```
|
||||
composer require mix/vega
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
- Swoole 多进程 (异步) 中使用
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
|
||||
$http = new Swoole\Http\Server('0.0.0.0', 9501);
|
||||
$http->on('Request', $vega->handler());
|
||||
$http->set([
|
||||
'worker_num' => 4,
|
||||
]);
|
||||
$http->start();
|
||||
```
|
||||
|
||||
开启多进程协程
|
||||
|
||||
```php
|
||||
$http->on('Request', $vega->handler());
|
||||
$http->on('WorkerStart', function ($server, $workerId) {
|
||||
// 协程初始化
|
||||
// 比如:启动 mix/database mix/redis 的连接池
|
||||
});
|
||||
$http->set([
|
||||
'enable_coroutine' => true,
|
||||
'worker_num' => 4,
|
||||
]);
|
||||
```
|
||||
|
||||
```
|
||||
php swoole.php
|
||||
```
|
||||
|
||||
- Swoole 单进程 (协程) 中使用
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
Swoole\Coroutine\run(function () {
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
|
||||
$server = new Swoole\Coroutine\Http\Server('0.0.0.0', 9502, false);
|
||||
$server->handle('/', $vega->handler());
|
||||
$server->start();
|
||||
});
|
||||
```
|
||||
|
||||
```
|
||||
php swooleco.php
|
||||
```
|
||||
|
||||
- WorkerMan 中使用
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
|
||||
$http_worker = new Workerman\Worker("http://0.0.0.0:2345");
|
||||
$http_worker->onMessage = $vega->handler();
|
||||
$http_worker->count = 4;
|
||||
Workerman\Worker::runAll();
|
||||
```
|
||||
|
||||
```
|
||||
php wokerman.php start
|
||||
```
|
||||
|
||||
- PHP-FPM 中使用
|
||||
|
||||
在 `nginx` 配置 `rewrite` 重写到 `index.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
return $vega->run();
|
||||
```
|
||||
|
||||
- PHP [cli-server](https://www.php.net/manual/zh/features.commandline.webserver.php) 中使用
|
||||
|
||||
这个内置的Web服务器主要用于本地开发使用,不可用于线上产品环境。
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
return $vega->run();
|
||||
```
|
||||
|
||||
```
|
||||
php -S localhost:8000 router.php
|
||||
```
|
||||
|
||||
- 访问测试
|
||||
|
||||
~~~
|
||||
% curl http://127.0.0.1:9501/hello
|
||||
hello, world!
|
||||
~~~
|
||||
|
||||
## 路由配置
|
||||
|
||||
配置 `Closure` 闭包路由
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
配置 `callable` 路由
|
||||
|
||||
```php
|
||||
class Hello {
|
||||
public function index(Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
}
|
||||
}
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', [new Hello(), 'index'])->methods('GET');
|
||||
```
|
||||
|
||||
配置路由变量
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/users/{id:\d+}', function (Mix\Vega\Context $ctx) {
|
||||
$id = $ctx->param('id');
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
配置多个 `method`
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/hello', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET', 'POST');
|
||||
```
|
||||
|
||||
## 路由前缀 (分组)
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$sub = $vega->pathPrefix('/foo');
|
||||
$sub->handle('/bar1', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
$sub->handle('/bar2', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello1, world!');
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
## 参数获取
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->request: ServerRequestInterface | 符合PSR的请求对象 |
|
||||
| $ctx->response: ResponseInterface | 符合PSR的响应对象 |
|
||||
| $ctx->param(string $key): string | 获取路由参数 |
|
||||
| $ctx->query(string $key): string | 获取url参数,包含路由参数 |
|
||||
| $ctx->defaultQuery(string $key, string $default): string | 获取url参数,可配置默认值 |
|
||||
| $ctx->getQuery(string $key): string or null | 获取url参数, 可判断是否存在 |
|
||||
| $ctx->postForm(string $key): string | 获取post参数 |
|
||||
| $ctx->defaultPostForm(string $key, string $default): string | 获取post参数,可配置默认值 |
|
||||
| $ctx->getPostForm(string $key): string or null | 获取post参数,可判断是否存在 |
|
||||
|
||||
### Headers, Cookies, Uri ...
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->method(): string | 请求类型 |
|
||||
| $ctx->contentType(): string | 文档类型 |
|
||||
| $ctx->header(string $key): string | 请求头 |
|
||||
| $ctx->cookie(string $name): string | cookies |
|
||||
| $ctx->uri(): UriInterface | 完整uri |
|
||||
| $ctx->rawData(): string | 原始包数据 |
|
||||
|
||||
### 客户端IP
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->clientIP(): string | 从反向代理获取用户真实IP |
|
||||
| $ctx->remoteIP(): string | 获取远程IP |
|
||||
|
||||
## 上传文件处理
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->formFile(string $name): UploadedFileInterface | 获取上传的第一个文件 |
|
||||
| $ctx->multipartForm(): UploadedFileInterface[] | 获取上传的全部文件 |
|
||||
|
||||
文件保存
|
||||
|
||||
```php
|
||||
$file = $ctx->formFile('img');
|
||||
$targetPath = '/data/project/public/uploads/' . $file->getClientFilename();
|
||||
$file->moveTo($targetPath);
|
||||
```
|
||||
|
||||
## 请求上下文
|
||||
|
||||
请求当中需要保存一些信息,比如:会话、JWT载荷等。
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->set(string $key, $value): void | 设置值 |
|
||||
| $ctx->get(string $key): mixed or null | 获取值 |
|
||||
| $ctx->mustGet(string $key): mixed or throws | 获取值或抛出异常 |
|
||||
|
||||
## 中断执行
|
||||
|
||||
`abort` 执行后,会停止执行后面的全部代码,包括中间件。
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->abort(): void | 中断,需自行处理响应 |
|
||||
| $ctx->abortWithStatus(int $code): void | 中断并响应状态码 |
|
||||
| $ctx->abortWithStatusJSON(int $code, $data): void | 中断并响应JSON |
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/users/{id}', function (Mix\Vega\Context $ctx) {
|
||||
if (true) {
|
||||
$ctx->string(401, 'Unauthorized');
|
||||
$ctx->abort();
|
||||
}
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
## 响应处理
|
||||
|
||||
| 方法名称 | 描述 |
|
||||
| ---- | ---- |
|
||||
| $ctx->status(int $code): void | 设置状态码 |
|
||||
| $ctx->setHeader(string $key, string $value): void | 设置header |
|
||||
| $ctx->setCookie(string $name, string $value, int $expire = 0, ...): void | 设置cookie |
|
||||
| $ctx->redirect(string $location, int $code = 302): void | 重定向 |
|
||||
|
||||
## JSON 请求与输出
|
||||
|
||||
获取 JSON 请求数据
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/users', function (Mix\Vega\Context $ctx) {
|
||||
$obj = $ctx->getJSON();
|
||||
if (!$obj) {
|
||||
throw new \Exception('Parameter error');
|
||||
}
|
||||
var_dump($obj);
|
||||
$ctx->JSON(200, [
|
||||
'code' => 0,
|
||||
'message' => 'ok'
|
||||
]);
|
||||
})->methods('POST');
|
||||
```
|
||||
|
||||
`mustGetJSON` 自带有效性检查,以下代码等同于上面
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/users', function (Mix\Vega\Context $ctx) {
|
||||
$obj = $ctx->mustGetJSON();
|
||||
var_dump($obj);
|
||||
$ctx->JSON(200, [
|
||||
'code' => 0,
|
||||
'message' => 'ok'
|
||||
]);
|
||||
})->methods('POST');
|
||||
```
|
||||
|
||||
### JSONP 处理
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handle('/jsonp', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->JSONP(200, [
|
||||
'code' => 0,
|
||||
'message' => 'ok'
|
||||
]);
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
## HTML 视图渲染
|
||||
|
||||
创建视图文件 `foo.php`
|
||||
|
||||
```php
|
||||
<p>id: <?= $id ?>, name: <?= $name ?></p>
|
||||
<p>friends:</p>
|
||||
<ul>
|
||||
<?php foreach($friends as $name): ?>
|
||||
<li><?= $name ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
```
|
||||
|
||||
配置视图路径,并响应html
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->withHTMLRoot('/data/project/views');
|
||||
$vega->handle('/html', function (Mix\Vega\Context $ctx) {
|
||||
$ctx->HTML(200, 'foo', [
|
||||
'id' => 1000,
|
||||
'name' => '小明',
|
||||
'friends' => [
|
||||
'小花',
|
||||
'小红'
|
||||
]
|
||||
]);
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
## 静态文件处理
|
||||
|
||||
基于 `sendfile` 零拷贝,不支持在 `PHP-FPM` 中使用
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->static('/static', '/data/project/public/static');
|
||||
$vega->staticFile('/favicon.ico', '/data/project/public/favicon.ico');
|
||||
```
|
||||
|
||||
## 设置中间件
|
||||
|
||||
给某个路由配置中间件,可配置多个
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$func = function (Mix\Vega\Context $ctx) {
|
||||
// do something
|
||||
$ctx->next();
|
||||
};
|
||||
$vega->handle('/hello', $func, function (Mix\Vega\Context $ctx) {
|
||||
$ctx->string(200, 'hello, world!');
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
配置全局中间件,即便没有匹配到路由也会执行
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->use(function (Mix\Vega\Context $ctx) {
|
||||
$ctx->next();
|
||||
});
|
||||
```
|
||||
|
||||
前置中间件
|
||||
|
||||
```php
|
||||
$vega->use(function (Mix\Vega\Context $ctx) {
|
||||
// do something
|
||||
$ctx->next();
|
||||
});
|
||||
```
|
||||
|
||||
后置中间件
|
||||
|
||||
```php
|
||||
$vega->use(function (Mix\Vega\Context $ctx) {
|
||||
$ctx->next();
|
||||
// do something
|
||||
});
|
||||
```
|
||||
|
||||
### 404 自定义
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->use(function (Mix\Vega\Context $ctx) {
|
||||
try{
|
||||
$ctx->next();
|
||||
} catch (Mix\Vega\Exception\NotFoundException $ex) {
|
||||
$ctx->string(404, 'New 404 response');
|
||||
$ctx->abort();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 500 全局异常捕获
|
||||
|
||||
```php
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->use(function (Mix\Vega\Context $ctx) {
|
||||
try{
|
||||
$ctx->next();
|
||||
} catch (\Throwable $ex) {
|
||||
if ($ex instanceof Mix\Vega\Abort || $ex instanceof Mix\Vega\Exception\NotFoundException) {
|
||||
throw $ex;
|
||||
}
|
||||
$ctx->string(500, 'New 500 response');
|
||||
$ctx->abort();
|
||||
}
|
||||
});
|
||||
```
|
96
docs/3.0/zh-cn/mix-websocket.md
Normal file
96
docs/3.0/zh-cn/mix-websocket.md
Normal file
@ -0,0 +1,96 @@
|
||||
## Mix WebSocket
|
||||
|
||||
基于 Swoole 协程的 PHP WebSocket 服务器与客户端
|
||||
|
||||
## 简介
|
||||
|
||||
该 WebSocket 支持处理服务器和客户端,服务器基于 Swoole 单进程协程 `Swoole\Coroutine\Http\Server` 驱动,没有多进程那些复杂的作用域和生命周期概念,开发体验和 Golang 一致,简单又高效。
|
||||
|
||||
**推荐搭配以下数据库使用 (支持协程和连接池):**
|
||||
|
||||
- https://github.com/mix-php/database
|
||||
- https://github.com/mix-php/redis
|
||||
|
||||
**推荐搭配以下库处理 Subscribe:**
|
||||
|
||||
- https://github.com/mix-php/redis-subscriber
|
||||
|
||||
## 安装
|
||||
|
||||
- Swoole >= 4.4.15: https://wiki.swoole.com/#/environment
|
||||
|
||||
```
|
||||
composer require mix/websocket
|
||||
```
|
||||
|
||||
## 服务器 Server
|
||||
|
||||
在 [Mix Vega](https://github.com/mix-php/vega) 中使用 (只支持 Swoole 单进程协程)
|
||||
|
||||
```php
|
||||
$upgrader = new Mix\WebSocket\Upgrader();
|
||||
|
||||
$vega = new Mix\Vega\Engine();
|
||||
$vega->handleFunc('/websocket', function (Mix\Vega\Context $ctx) use ($upgrader) {
|
||||
// 升级连接
|
||||
$conn = $upgrader->upgrade($ctx->request, $ctx->response);
|
||||
|
||||
// 接收消息
|
||||
$in = $conn->readMessage();
|
||||
var_dump($in->data);
|
||||
|
||||
// 发送消息
|
||||
$out = new \Swoole\WebSocket\Frame();
|
||||
$out->data = sprintf('hello, %s', $in->data);
|
||||
$conn->send($out);
|
||||
|
||||
$conn->close();
|
||||
})->methods('GET');
|
||||
```
|
||||
|
||||
在 Swoole 原生中使用 (只支持单进程协程)
|
||||
|
||||
```php
|
||||
Swoole\Coroutine\run(function () {
|
||||
$upgrader = new Mix\WebSocket\Upgrader();
|
||||
$server = new Swoole\Coroutine\Http\Server('0.0.0.0', 9502, false);
|
||||
$server->handle('/websocket', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($upgrader) {
|
||||
// 升级连接
|
||||
$conn = $upgrader->upgradeRaw($request, $response);
|
||||
|
||||
// ...
|
||||
});
|
||||
$server->start();
|
||||
});
|
||||
```
|
||||
|
||||
获取当前连接数
|
||||
|
||||
```php
|
||||
$total = $upgrader->count();
|
||||
```
|
||||
|
||||
关闭全部连接
|
||||
|
||||
```php
|
||||
$upgrader->closeAll();
|
||||
```
|
||||
|
||||
## 客户端 Client
|
||||
|
||||
可以连接任何 websocket v13 的服务器
|
||||
|
||||
```php
|
||||
$cli = Mix\WebSocket\Client('ws://127.0.0.1:9502/websocket');
|
||||
|
||||
// 发送消息
|
||||
$out = new \Swoole\WebSocket\Frame();
|
||||
$out->data = 'xiaoming';
|
||||
$cli->writeMessage($out);
|
||||
|
||||
// 接收消息
|
||||
$in = $cli->readMessage();
|
||||
var_dump($in->data);
|
||||
|
||||
$cli->close();
|
||||
```
|
113
docs/3.0/zh-cn/mix-worker-pool.md
Normal file
113
docs/3.0/zh-cn/mix-worker-pool.md
Normal file
@ -0,0 +1,113 @@
|
||||
## Mix Worker Pool
|
||||
|
||||
基于 Swoole 的工作池,协程池
|
||||
|
||||
> go 版本:https://github.com/mix-go/xwp
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
composer require mix/worker-pool
|
||||
```
|
||||
|
||||
## 单次调度
|
||||
|
||||
- 如果不想阻塞执行,可以使用 `$pool->start()` 启动
|
||||
|
||||
```php
|
||||
$jobQueue = new Swoole\Coroutine\Channel(200);
|
||||
$maxWorkers = 100;
|
||||
$handler = function ($data) {
|
||||
// do something
|
||||
};
|
||||
$pool = new Mix\WorkerPool\WorkerPool($jobQueue, $maxWorkers, $handler);
|
||||
|
||||
go(function () use ($jobQueue, $pool) {
|
||||
// 投放任务
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$jobQueue->push($i);
|
||||
}
|
||||
// 停止
|
||||
$pool->stop();
|
||||
});
|
||||
|
||||
$pool->run(); // 阻塞等待
|
||||
```
|
||||
|
||||
上面是采用闭包处理任务,也可以使用对象处理任务
|
||||
|
||||
```php
|
||||
class FooHandler implements \Mix\WorkerPool\RunInterface
|
||||
{
|
||||
public function do($data): void
|
||||
{
|
||||
// do something
|
||||
}
|
||||
}
|
||||
$pool = new Mix\WorkerPool\WorkerPool($jobQueue, $maxWorkers, new FooHandler());
|
||||
```
|
||||
|
||||
## 常驻调度
|
||||
|
||||
> 适合处理 MQ 队列的异步消费
|
||||
|
||||
以 Redis 作为 MQ 为例:
|
||||
|
||||
```php
|
||||
$maxWorkers = 20;
|
||||
$maxQueue = 10;
|
||||
$jobQueue = new Swoole\Coroutine\Channel($maxQueue);
|
||||
$handler = function ($data) {
|
||||
// do something
|
||||
};
|
||||
$pool = new Mix\WorkerPool\WorkerPool($jobQueue, $maxWorkers, $handler);
|
||||
|
||||
$quit = new Swoole\Coroutine\Channel();
|
||||
foreach ([SIGHUP, SIGINT, SIGTERM] as $signal) {
|
||||
Swoole\Process::signal($signal, function () use ($quit) {
|
||||
$quit->push(true);
|
||||
});
|
||||
}
|
||||
|
||||
go(function () use ($jobQueue, $pool, $quit) {
|
||||
// 投放任务
|
||||
while (true) {
|
||||
if (!$quit->isEmpty()) {
|
||||
$pool->stop();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$data = $redis->brPop(['test'], 1);
|
||||
} catch (\Throwable $ex) {
|
||||
// print log
|
||||
$pool->stop();
|
||||
return;
|
||||
}
|
||||
if (!$data) {
|
||||
continue;
|
||||
}
|
||||
$data = array_pop($data); // brPop命令最后一个键才是值
|
||||
$jobQueue->push($data);
|
||||
}
|
||||
});
|
||||
|
||||
$pool->run(); // 阻塞等待
|
||||
```
|
||||
|
||||
## 异常处理
|
||||
|
||||
闭包或者对象 `do` 方法中执行的代码,可能会抛出异常,必须要使用 `try/catch` 避免协程退出
|
||||
|
||||
```php
|
||||
class FooHandler implements \Mix\WorkerPool\RunInterface
|
||||
{
|
||||
public function do($data): void
|
||||
{
|
||||
try {
|
||||
// do something
|
||||
} catch (\Throwable $ex){
|
||||
// print log
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
17
docs/3.0/zh-cn/online-chating.md
Normal file
17
docs/3.0/zh-cn/online-chating.md
Normal file
@ -0,0 +1,17 @@
|
||||
## QQ 交流群
|
||||
|
||||
敲门暗号:`phper`
|
||||
|
||||
- OpenMix 技术交流 A 群: [284806582](https://shang.qq.com/wpa/qunwpa?idkey=b3a8618d3977cda4fed2363a666b081a31d89e3d31ab164497f53b72cf49968a)
|
||||
- OpenMix 技术交流 B 群: [825122875](http://shang.qq.com/wpa/qunwpa?idkey=d2908b0c7095fc7ec63a2391fa4b39a8c5cb16952f6cfc3f2ce4c9726edeaf20)
|
||||
|
||||
## 知乎
|
||||
|
||||
- 作者: https://www.zhihu.com/people/onanying
|
||||
- MixPHP 专栏: https://www.zhihu.com/column/mix-php
|
||||
|
||||
## 推荐阅读
|
||||
|
||||
- [MixPHP V3 开发流程体验 Swoole, Workerman, FPM, CLI-Server 多种运行模式介绍](https://zhuanlan.zhihu.com/p/398381870)
|
||||
- [MixPHP V3 增加了 PHP-FPM、CLI-Server 的支持](https://zhuanlan.zhihu.com/p/394059925)
|
||||
- [MixPHP V3 发布前的感想, 有哪些变化和特点](https://zhuanlan.zhihu.com/p/392558932)
|
42
docs/3.0/zh-cn/server-cli-server.md
Normal file
42
docs/3.0/zh-cn/server-cli-server.md
Normal file
@ -0,0 +1,42 @@
|
||||
# PHP Built-in CLI-Server
|
||||
|
||||
[CLI-Server](https://www.php.net/manual/zh/features.commandline.webserver.php) 是 PHP 内置的 Web 服务器,具有 `零扩展依赖` `热更新` `适合本机开发` 的特点。
|
||||
|
||||
## 入口文件
|
||||
|
||||
骨架路径 `public/index.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* PHP-FPM, cli-server 模式专用
|
||||
*/
|
||||
|
||||
use App\Vega;
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createUnsafeImmutable(__DIR__ . '/../', '.env')->load();
|
||||
define("APP_DEBUG", env('APP_DEBUG'));
|
||||
|
||||
App\Error::register();
|
||||
|
||||
return Vega::new()->run();
|
||||
```
|
||||
|
||||
## 启动服务
|
||||
|
||||
- API
|
||||
|
||||
```
|
||||
php -S localhost:8000 public/index.php
|
||||
```
|
||||
|
||||
- Web
|
||||
|
||||
支持静态文件处理
|
||||
|
||||
```
|
||||
php -S localhost:8000 -t public
|
||||
```
|
53
docs/3.0/zh-cn/server-php-fpm.md
Normal file
53
docs/3.0/zh-cn/server-php-fpm.md
Normal file
@ -0,0 +1,53 @@
|
||||
# PHP-FPM
|
||||
|
||||
PHP-FPM 部署具有 `热更新` `适合共享开发` `适合 admin 开发` 的特点,即便是在该传统模式下压测结果都接近 `Phalcon` 的性能。
|
||||
|
||||
## 入口文件
|
||||
|
||||
骨架路径 `public/index.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* PHP-FPM, cli-server 模式专用
|
||||
*/
|
||||
|
||||
use App\Vega;
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createUnsafeImmutable(__DIR__ . '/../', '.env')->load();
|
||||
define("APP_DEBUG", env('APP_DEBUG'));
|
||||
|
||||
App\Error::register();
|
||||
|
||||
return Vega::new()->run();
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
和 Laravel、ThinkPHP 部署方法完全一致,将 `public/index.php` 在 `nginx` 配置 `rewrite` 重写即可
|
||||
|
||||
```
|
||||
server {
|
||||
server_name www.domain.com;
|
||||
listen 80;
|
||||
root /data/project/public;
|
||||
index index.html index.php;
|
||||
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/(.*)$ /index.php/$1 last;
|
||||
}
|
||||
}
|
||||
|
||||
location ~ ^(.+\.php)(.*)$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
```
|
69
docs/3.0/zh-cn/server-swoole-coroutine.md
Normal file
69
docs/3.0/zh-cn/server-swoole-coroutine.md
Normal file
@ -0,0 +1,69 @@
|
||||
# Swoole Coroutine
|
||||
|
||||
[Swoole](https://www.swoole.com/) 驱动,该模式为单进程协程模型,非常适合编写 `WebSocket`、`gRPC` 服务。
|
||||
|
||||
## 入口文件
|
||||
|
||||
骨架路径 `bin/swooleco.php`
|
||||
|
||||
```php
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
ini_set('display_errors', 'on');
|
||||
ini_set('display_startup_errors', 'on');
|
||||
ini_set('error_reporting', E_ALL ^ E_NOTICE);
|
||||
ini_set('memory_limit', '1G');
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Container\Logger;
|
||||
use App\Vega;
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createUnsafeImmutable(__DIR__ . '/../', '.env')->load();
|
||||
define("APP_DEBUG", env('APP_DEBUG'));
|
||||
|
||||
App\Error::register();
|
||||
|
||||
Swoole\Coroutine\run(function () {
|
||||
$vega = Vega::new();
|
||||
$host = '0.0.0.0';
|
||||
$port = 9502;
|
||||
$server = new Swoole\Coroutine\Http\Server($host, $port, false, false);
|
||||
$server->handle('/', $vega->handler());
|
||||
|
||||
App\Container\DB::enableCoroutine();
|
||||
App\Container\RDS::enableCoroutine();
|
||||
|
||||
foreach ([SIGHUP, SIGINT, SIGTERM] as $signal) {
|
||||
Swoole\Process::signal($signal, function () use ($server) {
|
||||
Logger::instance()->info('Shutdown swoole coroutine server');
|
||||
$server->shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
printf("System Name: %s\n", strtolower(PHP_OS));
|
||||
printf("PHP Version: %s\n", PHP_VERSION);
|
||||
printf("Swoole Version: %s\n", swoole_version());
|
||||
printf("Listen Addr: http://%s:%d\n", $host, $port);
|
||||
Logger::instance()->info('Start swoole coroutine server');
|
||||
|
||||
$server->start();
|
||||
});
|
||||
```
|
||||
|
||||
## 启动服务
|
||||
|
||||
```
|
||||
php bin/swooleco.php
|
||||
```
|
78
docs/3.0/zh-cn/server-swoole.md
Normal file
78
docs/3.0/zh-cn/server-swoole.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Swoole
|
||||
|
||||
[Swoole](https://www.swoole.com/) 驱动,该模式为多进程模型。
|
||||
|
||||
- 同步模式:`常驻内存` `兼容 composer 生态`
|
||||
- 协程模式:`常驻内存` `协程性能强劲`
|
||||
|
||||
## 入口文件
|
||||
|
||||
骨架路径 `bin/swoole.php`,**默认为协程模式**,可手动修改为同步模式
|
||||
|
||||
```php
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
ini_set('display_errors', 'on');
|
||||
ini_set('display_startup_errors', 'on');
|
||||
ini_set('error_reporting', E_ALL ^ E_NOTICE);
|
||||
ini_set('memory_limit', '1G');
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Container\Logger;
|
||||
use App\Vega;
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createUnsafeImmutable(__DIR__ . '/../', '.env')->load();
|
||||
define("APP_DEBUG", env('APP_DEBUG'));
|
||||
|
||||
App\Error::register();
|
||||
|
||||
/**
|
||||
* 多进程默认开启了协程
|
||||
* 关闭协程只需关闭 `enable_coroutine` 配置并注释数据库的 `::enableCoroutine()` 即可退化为多进程同步模式
|
||||
*/
|
||||
|
||||
$vega = Vega::new();
|
||||
$host = '0.0.0.0';
|
||||
$port = 9501;
|
||||
$http = new Swoole\Http\Server($host, $port);
|
||||
$http->on('Request', $vega->handler());
|
||||
$http->on('WorkerStart', function ($server, $workerId) {
|
||||
// swoole 协程不支持 set_exception_handler 需要手动捕获异常
|
||||
try {
|
||||
App\Container\DB::enableCoroutine();
|
||||
App\Container\RDS::enableCoroutine();
|
||||
} catch (\Throwable $ex) {
|
||||
App\Error::handle($ex);
|
||||
}
|
||||
});
|
||||
$http->set([
|
||||
'enable_coroutine' => true,
|
||||
'worker_num' => 4,
|
||||
]);
|
||||
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
printf("System Name: %s\n", strtolower(PHP_OS));
|
||||
printf("PHP Version: %s\n", PHP_VERSION);
|
||||
printf("Swoole Version: %s\n", swoole_version());
|
||||
printf("Listen Addr: http://%s:%d\n", $host, $port);
|
||||
Logger::instance()->info('Start swoole server');
|
||||
|
||||
$http->start();
|
||||
```
|
||||
|
||||
## 启动服务
|
||||
|
||||
```
|
||||
php bin/swoole.php
|
||||
```
|
57
docs/3.0/zh-cn/server-workerman.md
Normal file
57
docs/3.0/zh-cn/server-workerman.md
Normal file
@ -0,0 +1,57 @@
|
||||
# WorkerMan
|
||||
|
||||
[WorkerMan](https://www.workerman.net/) 驱动,该模式为多进程模型,具有 `常驻内存` `兼容 composer 生态` 的特点。
|
||||
|
||||
## 入口文件
|
||||
|
||||
骨架路径 `bin/workerman.php`
|
||||
|
||||
```php
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
ini_set('display_errors', 'on');
|
||||
ini_set('display_startup_errors', 'on');
|
||||
ini_set('error_reporting', E_ALL ^ E_NOTICE);
|
||||
ini_set('memory_limit', '1G');
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use App\Container\Logger;
|
||||
use App\Vega;
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createUnsafeImmutable(__DIR__ . '/../', '.env')->load();
|
||||
define("APP_DEBUG", env('APP_DEBUG'));
|
||||
|
||||
App\Error::register();
|
||||
|
||||
$vega = Vega::new();
|
||||
$addr = 'http://0.0.0.0:2345';
|
||||
$http = new Workerman\Worker($addr);
|
||||
$http->onMessage = $vega->handler();
|
||||
$http->count = 4;
|
||||
|
||||
echo <<<EOL
|
||||
____
|
||||
______ ___ _____ ___ _____ / /_ _____
|
||||
/ __ `__ \/ /\ \/ /__ / __ \/ __ \/ __ \
|
||||
/ / / / / / / /\ \/ _ / /_/ / / / / /_/ /
|
||||
/_/ /_/ /_/_/ /_/\_\ / .___/_/ /_/ .___/
|
||||
/_/ /_/
|
||||
|
||||
|
||||
EOL;
|
||||
printf("System Name: %s\n", strtolower(PHP_OS));
|
||||
printf("PHP Version: %s\n", PHP_VERSION);
|
||||
printf("Workerman Version: %s\n", Workerman\Worker::VERSION);
|
||||
printf("Listen Addr: %s\n", $addr);
|
||||
Logger::instance()->info('Start workerman server');
|
||||
|
||||
Workerman\Worker::runAll();
|
||||
```
|
||||
|
||||
## 启动服务
|
||||
|
||||
```
|
||||
php bin/workerman.php start
|
||||
```
|
@ -1,3 +1,34 @@
|
||||
* 前言
|
||||
* 序言
|
||||
|
||||
* [项目介绍](zh-cn/README.md)
|
||||
* [技术交流](zh-cn/online-chating.md)
|
||||
* [性能压测](zh-cn/benchmarks.md)
|
||||
|
||||
* [项目介绍](zh-cn/README.md)
|
||||
* 快速开始
|
||||
|
||||
* [编写 CLI 程序](zh-cn/write-cli.md)
|
||||
* [编写 API 接口](zh-cn/write-api.md)
|
||||
* [编写 Web 页面](zh-cn/write-web.md)
|
||||
* [编写 WebSocket 服务](zh-cn/write-websocket.md)
|
||||
* [编写 gRPC 接口](zh-cn/write-grpc.md)
|
||||
|
||||
* 服务器
|
||||
|
||||
* [PHP Built-in CLI-Server](zh-cn/server-cli-server.md)
|
||||
* [PHP-FPM](zh-cn/server-php-fpm.md)
|
||||
* [Swoole](zh-cn/server-swoole.md)
|
||||
* [Swoole Coroutine](zh-cn/server-swoole-coroutine.md)
|
||||
* [WorkerMan](zh-cn/server-workerman.md)
|
||||
|
||||
* 独立模块
|
||||
|
||||
* [mix/vega](zh-cn/mix-vega.md)
|
||||
* [mix/database](zh-cn/mix-database.md)
|
||||
* [mix/redis](zh-cn/mix-redis.md)
|
||||
* [mix/redis-subscriber](zh-cn/mix-redis-subscriber.md)
|
||||
* [mix/grpc](zh-cn/mix-grpc.md)
|
||||
* [mix/websocket](zh-cn/mix-websocket.md)
|
||||
* [mix/cli](zh-cn/mix-cli.md)
|
||||
* [mix/worker-pool](zh-cn/mix-worker-pool.md)
|
||||
* [mix/validator](zh-cn/mix-validator.md)
|
||||
* [mix/event](zh-cn/mix-event.md)
|
||||
|
220
docs/3.0/zh-cn/write-api.md
Normal file
220
docs/3.0/zh-cn/write-api.md
Normal file
@ -0,0 +1,220 @@
|
||||
# 编写一个 API 接口
|
||||
|
||||
帮助你快速搭建 API 项目骨架,并指导你如何使用该骨架的细节,骨架默认开启了 SQL、Redis 日志,压测前请先关闭 `.env` 的 `APP_DEBUG`
|
||||
|
||||
## 安装
|
||||
|
||||
> 需要先安装 [Swoole](https://wiki.swoole.com/#/environment) 或者 [WorkerMan](http://doc.workerman.net/install/requirement.html)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/api-skeleton api
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
启动 [cli-server](https://www.php.net/manual/zh/features.commandline.webserver.php) 开发服务 (零依赖)
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 cliserver:start
|
||||
```
|
||||
|
||||
启动 Swoole 多进程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swoole:start
|
||||
```
|
||||
|
||||
启动 Swoole 协程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swooleco:start
|
||||
```
|
||||
|
||||
启动 WorkerMan 多进程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 workerman:start
|
||||
```
|
||||
|
||||
## 执行脚本
|
||||
|
||||
- `composer run-script` 命令中的 `--timeout=0` 参数是防止 composer [执行超时](https://getcomposer.org/doc/06-config.md#process-timeout)
|
||||
- `composer.json` 定义了命令执行脚本,对应上面的执行命令
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"cliserver:start": "php -S localhost:8000 public/index.php",
|
||||
"swoole:start": "php bin/swoole.php",
|
||||
"swooleco:start": "php bin/swooleco.php",
|
||||
"workerman:start": "php bin/workerman.php start",
|
||||
"cli:clearcache": "php bin/cli.php clearcache"
|
||||
}
|
||||
```
|
||||
|
||||
当然也可以直接下面这样启动,效果是一样的,但是 `scripts` 能帮你记录到底有哪些可用的命令,同时在IDE中调试更加方便。
|
||||
|
||||
```
|
||||
php bin/swoole.php start
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
- CLI
|
||||
|
||||
线上部署启动时,修改 `shell/server.sh` 脚本中的绝对路径和参数
|
||||
|
||||
```
|
||||
php=/usr/local/bin/php
|
||||
file=/project/bin/swoole.php
|
||||
cmd=start
|
||||
numprocs=1
|
||||
```
|
||||
|
||||
启动管理
|
||||
|
||||
```
|
||||
sh shell/server.sh start
|
||||
sh shell/server.sh stop
|
||||
sh shell/server.sh restart
|
||||
```
|
||||
|
||||
使用 `nginx` 或者 `SLB` 代理到服务器端口即可
|
||||
|
||||
```
|
||||
server {
|
||||
server_name www.domain.com;
|
||||
listen 80;
|
||||
root /data/project/public;
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "keep-alive";
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Scheme $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
if (!-f $request_filename) {
|
||||
proxy_pass http://127.0.0.1:9501;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- PHP-FPM
|
||||
|
||||
和 Laravel、ThinkPHP 部署方法完全一致,将 `public/index.php` 在 `nginx` 配置 `rewrite` 重写即可
|
||||
|
||||
```
|
||||
server {
|
||||
server_name www.domain.com;
|
||||
listen 80;
|
||||
root /data/project/public;
|
||||
index index.html index.php;
|
||||
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/(.*)$ /index.php/$1 last;
|
||||
}
|
||||
}
|
||||
|
||||
location ~ ^(.+\.php)(.*)$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 编写一个 API 接口
|
||||
|
||||
首先修改根目录 `.env` 文件的数据库信息
|
||||
|
||||
然后在 `routes/index.php` 定义一个新的路由
|
||||
|
||||
```php
|
||||
$vega->handle('/users/{id}', [new Users(), 'index'])->methods('GET');
|
||||
```
|
||||
|
||||
路由里使用了 `Users` 控制器,我们需要创建他
|
||||
|
||||
- 如何配置路由:[mix/vega](zh-cn/mix-vega.md)
|
||||
- 如何调用数据库:[mix/database](zh-cn/mix-database.md)
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Container\DB;
|
||||
use Mix\Vega\Context;
|
||||
|
||||
class Users
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Context $ctx
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index(Context $ctx)
|
||||
{
|
||||
$row = DB::instance()->table('users')->where('id = ?', $ctx->param('id'))->first();
|
||||
if (!$row) {
|
||||
throw new \Exception('User not found');
|
||||
}
|
||||
$ctx->JSON(200, [
|
||||
'code' => 0,
|
||||
'message' => 'ok',
|
||||
'data' => $row
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
重新启动服务器后方可测试新开发的接口
|
||||
|
||||
> 实际开发中使用 PhpStorm 的 Run 功能,只需要点击一下重启按钮即可
|
||||
|
||||
```
|
||||
// 查找进程 PID
|
||||
ps -ef | grep swoole
|
||||
|
||||
// 通过 PID 停止进程
|
||||
kill PID
|
||||
|
||||
// 重新启动进程
|
||||
composer run-script swoole:start
|
||||
|
||||
// curl 测试
|
||||
curl http://127.0.0.1:9501/users/1
|
||||
```
|
||||
|
||||
## 使用容器中的对象
|
||||
|
||||
容器采用了一个简单的单例模式,你可以修改为更加适合自己的方式。
|
||||
|
||||
- 数据库:[mix/database](zh-cn/mix-database.md)
|
||||
|
||||
```
|
||||
DB::instance()
|
||||
```
|
||||
|
||||
- Redis:[mix/redis](zh-cn/mix-redis.md)
|
||||
|
||||
```
|
||||
RDS::instance()
|
||||
```
|
||||
|
||||
- 日志:[monolog/monolog](https://seldaek.github.io/monolog/doc/01-usage.html)
|
||||
|
||||
```
|
||||
Logger::instance()
|
||||
```
|
||||
|
||||
- 配置:[hassankhan/config](https://github.com/hassankhan/config#getting-values)
|
||||
|
||||
```
|
||||
Config::instance()
|
||||
```
|
110
docs/3.0/zh-cn/write-cli.md
Normal file
110
docs/3.0/zh-cn/write-cli.md
Normal file
@ -0,0 +1,110 @@
|
||||
# 编写一个 CLI 程序
|
||||
|
||||
帮助你快速搭建 CLI 项目骨架,并指导你如何使用该骨架的细节,骨架默认开启了 SQL、Redis 日志,压测前请先关闭 `.env` 的 `APP_DEBUG`
|
||||
|
||||
## 安装
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/cli-skeleton cli
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
使用 `composer` 执行命令
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 cli:clearcache
|
||||
```
|
||||
|
||||
## 执行脚本
|
||||
|
||||
- `composer run-script` 命令中的 `--timeout=0` 参数是防止 composer [执行超时](https://getcomposer.org/doc/06-config.md#process-timeout)
|
||||
- `composer.json` 定义了命令执行脚本,对应上面的执行命令
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"cli:clearcache": "php bin/cli.php clearcache"
|
||||
}
|
||||
```
|
||||
|
||||
当然也可以直接下面这样启动,效果是一样的,但是 `scripts` 能帮你记录到底有哪些可用的命令,同时在IDE中调试更加方便。
|
||||
|
||||
```
|
||||
php bin/cli.php clearcache
|
||||
```
|
||||
|
||||
## 编写一个 CLI 程序
|
||||
|
||||
首先我们在 `bin/cli.php` 入口文件中增加一个命令
|
||||
|
||||
- 如何配置命令:[mix/cli](https://github.com/mix-php/cli#readme)
|
||||
|
||||
```php
|
||||
Cli::setName('app')->setVersion('0.0.0-alpha');
|
||||
$cmds = [
|
||||
new Mix\Cli\Command([
|
||||
'name' => 'clearcache',
|
||||
'short' => 'Clear cache',
|
||||
'options' => [
|
||||
new Mix\Cli\Option([
|
||||
'names' => ['k', 'key'],
|
||||
'usage' => 'Key name'
|
||||
]),
|
||||
],
|
||||
'run' => new App\Command\ClearCache(),
|
||||
])
|
||||
];
|
||||
Cli::addCommand(...$cmds)->run();
|
||||
```
|
||||
|
||||
查看命令帮助,检查配置是否正确
|
||||
|
||||
```
|
||||
$ php bin/cli.php
|
||||
Usage: bin/cli.php [OPTIONS] COMMAND [ARG...]
|
||||
|
||||
Commands:
|
||||
clearcache Clear cache
|
||||
|
||||
Global Options:
|
||||
-h, --help Print usage
|
||||
-v, --version Print version information
|
||||
|
||||
Run 'bin/cli.php COMMAND --help' for more information on a command.
|
||||
|
||||
Developed with Mix PHP framework. (openmix.org/mix-php)
|
||||
```
|
||||
|
||||
执行 `clearcache` 命令
|
||||
|
||||
```
|
||||
$ php bin/cli.php clearcache
|
||||
```
|
||||
|
||||
## 使用容器中的对象
|
||||
|
||||
容器采用了一个简单的单例模式,你可以修改为更加适合自己的方式。
|
||||
|
||||
- 数据库:[mix/database](zh-cn/mix-database.md)
|
||||
|
||||
```
|
||||
DB::instance()
|
||||
```
|
||||
|
||||
- Redis:[mix/redis](zh-cn/mix-redis.md)
|
||||
|
||||
```
|
||||
RDS::instance()
|
||||
```
|
||||
|
||||
- 日志:[monolog/monolog](https://seldaek.github.io/monolog/doc/01-usage.html)
|
||||
|
||||
```
|
||||
Logger::instance()
|
||||
```
|
||||
|
||||
- 配置:[hassankhan/config](https://github.com/hassankhan/config#getting-values)
|
||||
|
||||
```
|
||||
Config::instance()
|
||||
```
|
166
docs/3.0/zh-cn/write-grpc.md
Normal file
166
docs/3.0/zh-cn/write-grpc.md
Normal file
@ -0,0 +1,166 @@
|
||||
# 编写一个 gRPC 接口
|
||||
|
||||
帮助你快速搭建 gRPC 项目骨架,并指导你如何使用该骨架的细节,骨架默认开启了 SQL、Redis 日志,压测前请先关闭 `.env` 的 `APP_DEBUG`
|
||||
|
||||
## 安装
|
||||
|
||||
> 需要先安装 [Swoole](https://wiki.swoole.com/#/environment)
|
||||
|
||||
- Swoole >= 4.4.4: https://wiki.swoole.com/#/environment
|
||||
- 需要开启 `--enable-http2`
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/grpc-skeleton grpc
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
启动 Swoole 多进程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swoole:start
|
||||
```
|
||||
|
||||
启动 Swoole 协程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swooleco:start
|
||||
```
|
||||
|
||||
## 执行脚本
|
||||
|
||||
- `composer run-script` 命令中的 `--timeout=0` 参数是防止 composer [执行超时](https://getcomposer.org/doc/06-config.md#process-timeout)
|
||||
- `composer.json` 定义了命令执行脚本,对应上面的执行命令
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"swoole:start": "php bin/swoole.php",
|
||||
"swooleco:start": "php bin/swooleco.php",
|
||||
"cli:clearcache": "php bin/cli.php clearcache"
|
||||
}
|
||||
```
|
||||
|
||||
当然也可以直接下面这样启动,效果是一样的,但是 `scripts` 能帮你记录到底有哪些可用的命令,同时在IDE中调试更加方便。
|
||||
|
||||
```
|
||||
php bin/swoole.php start
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
线上部署启动时,修改 `shell/server.sh` 脚本中的绝对路径和参数
|
||||
|
||||
```
|
||||
php=/usr/local/bin/php
|
||||
file=/project/bin/swoole.php
|
||||
cmd=start
|
||||
numprocs=1
|
||||
```
|
||||
|
||||
启动管理
|
||||
|
||||
```
|
||||
sh shell/server.sh start
|
||||
sh shell/server.sh stop
|
||||
sh shell/server.sh restart
|
||||
```
|
||||
|
||||
gRPC 通常都是内部使用,使用内网 `SLB` 代理到服务器IP或者直接使用 IP:PORT 调用
|
||||
|
||||
## 编写一个 gRPC 接口
|
||||
|
||||
首先修改根目录 `.env` 文件的数据库信息
|
||||
|
||||
然后在 `proto` 目录创建 `greeter.proto` 文件,并根据 [使用说明](https://github.com/mix-php/grpc#%E9%80%9A%E8%BF%87-proto-%E7%94%9F%E6%88%90-php-%E4%BB%A3%E7%A0%81) 将 .proto 文件生成为 PHP 代码
|
||||
|
||||
```
|
||||
protoc --php_out=. --mix_out=. greeter.proto
|
||||
```
|
||||
|
||||
然后创建一个新的服务 `src/Service/Say.php`
|
||||
|
||||
- `Say` 类实现了代码生成器生成的 `Php\Micro\Grpc\Greeter\SayInterface` 接口
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Mix\Grpc\Context;
|
||||
use Php\Micro\Grpc\Greeter\Request;
|
||||
use Php\Micro\Grpc\Greeter\Response;
|
||||
|
||||
/**
|
||||
* Class Say
|
||||
* @package App\Service
|
||||
*/
|
||||
class Say implements \Php\Micro\Grpc\Greeter\SayInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Context $context
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function Hello(Context $context, Request $request): Response
|
||||
{
|
||||
$response = new Response();
|
||||
$response->setMsg(sprintf('hello, %s', $request->getName()));
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在 `src/Grpc.php` 中将服务注册到服务器
|
||||
|
||||
```php
|
||||
$server->register(Say::class);
|
||||
```
|
||||
|
||||
重新启动服务器后方可测试新开发的接口
|
||||
|
||||
> 实际开发中使用 PhpStorm 的 Run 功能,只需要点击一下重启按钮即可
|
||||
|
||||
```
|
||||
// 查找进程 PID
|
||||
ps -ef | grep swoole
|
||||
|
||||
// 通过 PID 停止进程
|
||||
kill PID
|
||||
|
||||
// 重新启动进程
|
||||
composer run-script swoole:start
|
||||
```
|
||||
|
||||
## 如何使用 gRPC 客户端
|
||||
|
||||
- [mix/grpc#客户端调用一个 gRPC 服务](https://gitee.com/mix-php/mix/tree/master/src/grpc#%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B0%83%E7%94%A8%E4%B8%80%E4%B8%AA-grpc-%E6%9C%8D%E5%8A%A1)
|
||||
|
||||
## 使用容器中的对象
|
||||
|
||||
容器采用了一个简单的单例模式,你可以修改为更加适合自己的方式。
|
||||
|
||||
- 数据库:[mix/database](zh-cn/mix-database.md)
|
||||
|
||||
```
|
||||
DB::instance()
|
||||
```
|
||||
|
||||
- Redis:[mix/redis](zh-cn/mix-redis.md)
|
||||
|
||||
```
|
||||
RDS::instance()
|
||||
```
|
||||
|
||||
- 日志:[monolog/monolog](https://seldaek.github.io/monolog/doc/01-usage.html)
|
||||
|
||||
```
|
||||
Logger::instance()
|
||||
```
|
||||
|
||||
- 配置:[hassankhan/config](https://github.com/hassankhan/config#getting-values)
|
||||
|
||||
```
|
||||
Config::instance()
|
||||
```
|
221
docs/3.0/zh-cn/write-web.md
Normal file
221
docs/3.0/zh-cn/write-web.md
Normal file
@ -0,0 +1,221 @@
|
||||
# 编写一个 Web 页面
|
||||
|
||||
帮助你快速搭建 Web 项目骨架,并指导你如何使用该骨架的细节,骨架默认开启了 SQL、Redis 日志,压测前请先关闭 `.env` 的 `APP_DEBUG`
|
||||
|
||||
## 安装
|
||||
|
||||
> 需要先安装 [Swoole](https://wiki.swoole.com/#/environment) 或者 [WorkerMan](http://doc.workerman.net/install/requirement.html)
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/web-skeleton web
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
启动 [cli-server](https://www.php.net/manual/zh/features.commandline.webserver.php) 开发服务 (零依赖)
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 cliserver:start
|
||||
```
|
||||
|
||||
启动 Swoole 多进程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swoole:start
|
||||
```
|
||||
|
||||
启动 Swoole 协程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swooleco:start
|
||||
```
|
||||
|
||||
启动 WorkerMan 多进程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 workerman:start
|
||||
```
|
||||
|
||||
## 执行脚本
|
||||
|
||||
- `composer run-script` 命令中的 `--timeout=0` 参数是防止 composer [执行超时](https://getcomposer.org/doc/06-config.md#process-timeout)
|
||||
- `composer.json` 定义了命令执行脚本,对应上面的执行命令
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"cliserver:start": "php -S localhost:8000 -t public",
|
||||
"swoole:start": "php bin/swoole.php",
|
||||
"swooleco:start": "php bin/swooleco.php",
|
||||
"workerman:start": "php bin/workerman.php start",
|
||||
"cli:clearcache": "php bin/cli.php clearcache"
|
||||
}
|
||||
```
|
||||
|
||||
当然也可以直接下面这样启动,效果是一样的,但是 `scripts` 能帮你记录到底有哪些可用的命令,同时在IDE中调试更加方便。
|
||||
|
||||
```
|
||||
php bin/swoole.php start
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
- CLI
|
||||
|
||||
线上部署启动时,修改 `shell/server.sh` 脚本中的绝对路径和参数
|
||||
|
||||
```
|
||||
php=/usr/local/bin/php
|
||||
file=/project/bin/swoole.php
|
||||
cmd=start
|
||||
numprocs=1
|
||||
```
|
||||
|
||||
启动管理
|
||||
|
||||
```
|
||||
sh shell/server.sh start
|
||||
sh shell/server.sh stop
|
||||
sh shell/server.sh restart
|
||||
```
|
||||
|
||||
使用 `nginx` 或者 `SLB` 代理到服务器端口即可
|
||||
|
||||
```
|
||||
server {
|
||||
server_name www.domain.com;
|
||||
listen 80;
|
||||
root /data/project/public;
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "keep-alive";
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Scheme $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
if (!-f $request_filename) {
|
||||
proxy_pass http://127.0.0.1:9501;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- PHP-FPM
|
||||
|
||||
和 Laravel、ThinkPHP 部署方法完全一致,将 `public/index.php` 在 `nginx` 配置 `rewrite` 重写即可
|
||||
|
||||
```
|
||||
server {
|
||||
server_name www.domain.com;
|
||||
listen 80;
|
||||
root /data/project/public;
|
||||
index index.html index.php;
|
||||
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^/(.*)$ /index.php/$1 last;
|
||||
}
|
||||
}
|
||||
|
||||
location ~ ^(.+\.php)(.*)$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_split_path_info ^(.+\.php)(.*)$;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 编写一个 Web 页面
|
||||
|
||||
首先修改根目录 `.env` 文件的数据库信息
|
||||
|
||||
然后在 `routes/index.php` 定义一个新的路由
|
||||
|
||||
```php
|
||||
$vega->handle('/', [new Hello(), 'index'])->methods('GET');
|
||||
```
|
||||
|
||||
路由里使用了 `Hello` 控制器,我们需要创建他
|
||||
|
||||
- 如何配置路由:[mix/vega](zh-cn/mix-vega.md)
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Mix\Vega\Context;
|
||||
|
||||
class Hello
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Context $ctx
|
||||
*/
|
||||
public function index(Context $ctx)
|
||||
{
|
||||
$ctx->HTML(200, 'index', [
|
||||
'title' => 'Hello, World!'
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在 `views` 目录创建 `index.php` 视图文件
|
||||
|
||||
```php
|
||||
<html>
|
||||
<h1>
|
||||
<?= $title ?>
|
||||
</h1>
|
||||
</html>
|
||||
```
|
||||
|
||||
重新启动服务器后方可测试新开发的接口
|
||||
|
||||
> 实际开发中使用 PhpStorm 的 Run 功能,只需要点击一下重启按钮即可
|
||||
|
||||
```
|
||||
// 查找进程 PID
|
||||
ps -ef | grep swoole
|
||||
|
||||
// 通过 PID 停止进程
|
||||
kill PID
|
||||
|
||||
// 重新启动进程
|
||||
composer run-script swoole:start
|
||||
|
||||
// curl 测试
|
||||
curl http://127.0.0.1:9501/
|
||||
```
|
||||
|
||||
## 使用容器中的对象
|
||||
|
||||
容器采用了一个简单的单例模式,你可以修改为更加适合自己的方式。
|
||||
|
||||
- 数据库:[mix/database](zh-cn/mix-database.md)
|
||||
|
||||
```
|
||||
DB::instance()
|
||||
```
|
||||
|
||||
- Redis:[mix/redis](zh-cn/mix-redis.md)
|
||||
|
||||
```
|
||||
RDS::instance()
|
||||
```
|
||||
|
||||
- 日志:[monolog/monolog](https://seldaek.github.io/monolog/doc/01-usage.html)
|
||||
|
||||
```
|
||||
Logger::instance()
|
||||
```
|
||||
|
||||
- 配置:[hassankhan/config](https://github.com/hassankhan/config#getting-values)
|
||||
|
||||
```
|
||||
Config::instance()
|
||||
```
|
239
docs/3.0/zh-cn/write-websocket.md
Normal file
239
docs/3.0/zh-cn/write-websocket.md
Normal file
@ -0,0 +1,239 @@
|
||||
# 编写一个 WebSocket 服务
|
||||
|
||||
帮助你快速搭建 WebSocket 项目骨架,并指导你如何使用该骨架的细节,骨架默认开启了 SQL、Redis 日志,压测前请先关闭 `.env` 的 `APP_DEBUG`
|
||||
|
||||
## 安装
|
||||
|
||||
> 需要先安装 [Swoole](https://wiki.swoole.com/#/environment)
|
||||
|
||||
- Swoole >= 4.4.15: https://wiki.swoole.com/#/environment
|
||||
|
||||
```
|
||||
composer create-project --prefer-dist mix/websocket-skeleton websocket
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
启动 Swoole 协程服务
|
||||
|
||||
```
|
||||
composer run-script --timeout=0 swooleco:start
|
||||
```
|
||||
|
||||
## 执行脚本
|
||||
|
||||
- `composer run-script` 命令中的 `--timeout=0` 参数是防止 composer [执行超时](https://getcomposer.org/doc/06-config.md#process-timeout)
|
||||
- `composer.json` 定义了命令执行脚本,对应上面的执行命令
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"swooleco:start": "php bin/swooleco.php",
|
||||
"cli:clearcache": "php bin/cli.php clearcache"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
当然也可以直接下面这样启动,效果是一样的,但是 `scripts` 能帮你记录到底有哪些可用的命令,同时在IDE中调试更加方便。
|
||||
|
||||
```
|
||||
php bin/swooleco.php start
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
线上部署启动时,修改 `shell/server.sh` 脚本中的绝对路径和参数
|
||||
|
||||
```
|
||||
php=/usr/local/bin/php
|
||||
file=/project/bin/swooleco.php
|
||||
cmd=start
|
||||
numprocs=1
|
||||
```
|
||||
|
||||
启动管理
|
||||
|
||||
```
|
||||
sh shell/server.sh start
|
||||
sh shell/server.sh stop
|
||||
sh shell/server.sh restart
|
||||
```
|
||||
|
||||
使用 `nginx` 或者 `SLB` 代理到服务器端口即可
|
||||
|
||||
```
|
||||
location /websocket {
|
||||
proxy_pass http://127.0.0.1:9502;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
```
|
||||
|
||||
## 编写一个 WebSocket 服务
|
||||
|
||||
首先修改根目录 `.env` 文件的数据库信息
|
||||
|
||||
然后在 `routes/index.php` 定义一个新的路由
|
||||
|
||||
```php
|
||||
$vega->handle('/websocket', [new WebSocket(), 'index'])->methods('GET');
|
||||
```
|
||||
|
||||
路由里使用了 `WebSocket` 控制器,我们需要创建他
|
||||
|
||||
- 如何配置路由:[mix/vega](zh-cn/mix-vega.md)
|
||||
- 如何使用 WebSocket 升级器:[mix/websocket](https://github.com/mix-php/websocket#readme)
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Container\Upgrader;
|
||||
use App\Service\Session;
|
||||
use Mix\Vega\Context;
|
||||
|
||||
class WebSocket
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Context $ctx
|
||||
*/
|
||||
public function index(Context $ctx)
|
||||
{
|
||||
$conn = Upgrader::instance()->upgrade($ctx->request, $ctx->response);
|
||||
$session = new Session($conn);
|
||||
$session->start();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
控制器中使用了一个 `Session` 类来处理连接事务
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Handler\Hello;
|
||||
use Mix\WebSocket\Connection;
|
||||
use Swoole\Coroutine\Channel;
|
||||
|
||||
class Session
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $conn;
|
||||
|
||||
/**
|
||||
* @var Channel
|
||||
*/
|
||||
protected $writeChan;
|
||||
|
||||
/**
|
||||
* Session constructor.
|
||||
* @param Connection $conn
|
||||
*/
|
||||
public function __construct(Connection $conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->writeChan = new Channel(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
*/
|
||||
public function send(string $data): void
|
||||
{
|
||||
$this->writeChan->push($data);
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
// 接收消息
|
||||
go(function () {
|
||||
while (true) {
|
||||
$frame = $this->conn->recv();
|
||||
$message = $frame->data;
|
||||
|
||||
(new Hello($this))->index($message);
|
||||
}
|
||||
});
|
||||
|
||||
// 发送消息
|
||||
go(function () {
|
||||
while (true) {
|
||||
$data = $this->writeChan->pop();
|
||||
if (!$data) {
|
||||
return;
|
||||
}
|
||||
$frame = new \Swoole\WebSocket\Frame();
|
||||
$frame->data = $data;
|
||||
$frame->opcode = WEBSOCKET_OPCODE_TEXT; // or WEBSOCKET_OPCODE_BINARY
|
||||
$this->conn->send($frame);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在接收消息处,使用了 `src/Handler/Hello.php` 处理器对当前发送的消息做逻辑处理,我们只需根据自己的需求增加新的处理器来处理不同消息即可。
|
||||
|
||||
```
|
||||
(new Hello($this))->index($message);
|
||||
```
|
||||
|
||||
重新启动服务器后方可测试新开发的接口
|
||||
|
||||
> 实际开发中使用 PhpStorm 的 Run 功能,只需要点击一下重启按钮即可
|
||||
|
||||
```
|
||||
// 查找进程 PID
|
||||
ps -ef | grep swoole
|
||||
|
||||
// 通过 PID 停止进程
|
||||
kill PID
|
||||
|
||||
// 重新启动进程
|
||||
composer run-script swoole:start
|
||||
```
|
||||
|
||||
使用测试工具测试
|
||||
|
||||
- [WEBSOCKET 在线测试工具](http://www.easyswoole.com/wstool.html)
|
||||
|
||||
## 如何使用 WebSocket 客户端
|
||||
|
||||
- [mix-php/websocket#客户端-client](https://github.com/mix-php/websocket#%E5%AE%A2%E6%88%B7%E7%AB%AF-client)
|
||||
|
||||
## 使用容器中的对象
|
||||
|
||||
容器采用了一个简单的单例模式,你可以修改为更加适合自己的方式。
|
||||
|
||||
- 数据库:[mix/database](zh-cn/mix-database.md)
|
||||
|
||||
```
|
||||
DB::instance()
|
||||
```
|
||||
|
||||
- Redis:[mix/redis](zh-cn/mix-redis.md)
|
||||
|
||||
```
|
||||
RDS::instance()
|
||||
```
|
||||
|
||||
- 日志:[monolog/monolog](https://seldaek.github.io/monolog/doc/01-usage.html)
|
||||
|
||||
```
|
||||
Logger::instance()
|
||||
```
|
||||
|
||||
- 配置:[hassankhan/config](https://github.com/hassankhan/config#getting-values)
|
||||
|
||||
```
|
||||
Config::instance()
|
||||
```
|
@ -3,9 +3,28 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MixPHP 开发文档</title>
|
||||
<style>
|
||||
body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: #34495e;
|
||||
font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0;
|
||||
margin: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
h1 {
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 300;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
MixPHP 开发文档
|
||||
<h1>MixPHP 开发文档</h1>
|
||||
<ul>
|
||||
<li><a href="3.0">V3.0</a></li>
|
||||
<li><a href="https://www.kancloud.cn/onanying/mixphp2-2/content">V2.2</a></li>
|
||||
|
Loading…
Reference in New Issue
Block a user