├── .editorconfig ├── en └── .keep ├── zh-CN ├── image │ ├── extra │ │ ├── .keep │ │ ├── block.png │ │ ├── memory.png │ │ ├── trace.png │ │ ├── monitor.png │ │ ├── process.png │ │ └── swoft-whoops.jpg │ ├── tcp-server │ │ ├── .keep │ │ └── devtool-tcp-test.png │ ├── wx.png │ ├── aop │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── AOP.png │ │ └── OOP.png │ ├── ready │ │ ├── fpm.png │ │ ├── lnmp.png │ │ └── eventloop.png │ ├── bean │ │ ├── DIPUML.png │ │ └── NODIPUML.png │ ├── rpc-server │ │ ├── 1.png │ │ ├── 2.png │ │ └── 3.png │ ├── tool │ │ ├── migrate.png │ │ ├── UpConfirm.png │ │ ├── migrateAdd.png │ │ ├── migrateHis.png │ │ ├── migrateUp.png │ │ ├── migrateUpDb.png │ │ ├── migrateCreate.png │ │ ├── migrateMessge.png │ │ ├── migrateUpPrefix.png │ │ └── swoftcli │ │ │ ├── pack-phar.jpg │ │ │ ├── swoftcli-gen.png │ │ │ ├── swoftcli-home.png │ │ │ ├── swoftcli-new.png │ │ │ ├── hot-restart-help.jpg │ │ │ ├── run-hot-restart.jpg │ │ │ ├── swoftcli-new-app.png │ │ │ ├── swoftcli-new-cpt.png │ │ │ ├── swoftcli-new-app-example.png │ │ │ └── swoftcli-new-cpt-example.png │ ├── console │ │ ├── cli-app.jpg │ │ ├── cli-group.jpg │ │ ├── cli-command.jpg │ │ └── cli-version.jpg │ ├── ms │ │ ├── breaker_base.png │ │ ├── breaker_ext.png │ │ ├── limiter_leaky.png │ │ └── limiter_token.png │ ├── best │ │ └── architecture.png │ └── http-server │ │ ├── process │ │ └── 1.png │ │ └── swoft2-request-flow.png ├── notice │ ├── index.md │ ├── memory-leak.md │ ├── performance-optimization.md │ └── prohibited.md ├── best-practices │ ├── demo.md │ ├── deploy.md │ ├── index.md │ ├── middleware.md │ ├── architecture.md │ └── nginx-config.md ├── annotation │ ├── why.md │ ├── tool.md │ ├── usage.md │ └── index.md ├── rpc-server │ ├── request-response.md │ ├── setting.md │ ├── command.md │ ├── index.md │ └── statement.md ├── common │ ├── stdlib.md │ ├── index.md │ ├── timer.md │ ├── generic.md │ └── co.md ├── rpc-client │ ├── index.md │ ├── setting.md │ ├── rpc-1.0.md │ └── usage.md ├── container │ └── index.md ├── validator │ ├── index.md │ ├── setting.md │ ├── anywhere-validator.md │ ├── user-validator.md │ └── controller-validator.md ├── ms │ └── govern │ │ ├── index.md │ │ └── config.md ├── log │ ├── index.md │ └── clog.md ├── quick-start │ ├── development.md │ ├── index.md │ ├── install.md │ ├── start-swoft.md │ └── project-skeleton.md ├── process │ ├── index.md │ ├── process.md │ └── user-process.md ├── task │ ├── index.md │ ├── statement.md │ ├── coroutine.md │ ├── async.md │ ├── setting.md │ └── crontab.md ├── redis │ ├── index.md │ ├── pipeline.md │ ├── transaction.md │ └── pub-sub.md ├── db │ ├── index.md │ ├── origin.md │ ├── transaction.md │ └── selectDb.md ├── component │ ├── index.md │ ├── how-to-load.md │ ├── structure.md │ └── entry.md ├── i18n │ ├── index.md │ ├── setting.md │ └── usage.md ├── tool │ ├── swoftcli │ │ ├── index.md │ │ ├── install.md │ │ ├── generate-app-classs.md │ │ ├── hot-restart.md │ │ ├── build-phar.md │ │ └── create-app-or-component.md │ └── devtool │ │ ├── index.md │ │ └── entity.md ├── introduction │ ├── question.md │ └── swoft.md ├── config │ ├── index.md │ ├── env.md │ └── config.md ├── ready │ ├── index.md │ ├── composer.md │ ├── io.md │ ├── tradition.md │ └── swoole.md ├── bean │ ├── request.md │ ├── singleton.md │ ├── prototype.md │ └── interface.md ├── websocket-server │ ├── manage.md │ ├── index.md │ ├── message-send.md │ ├── exception.md │ ├── config.md │ └── module.md ├── tcp-server │ ├── manage.md │ ├── index.md │ ├── event.md │ ├── controller.md │ ├── config.md │ └── client-communicate.md ├── http-server │ ├── index.md │ ├── http.md │ ├── exception.md │ ├── command.md │ ├── response.md │ ├── setting.md │ └── controller.md ├── console │ ├── output.md │ ├── usage.md │ ├── index.md │ ├── config.md │ └── input.md ├── aop │ ├── order.md │ └── usage.md ├── error │ └── index.md ├── README.md ├── event │ ├── index.md │ ├── swoole-events.md │ └── usage.md ├── extra │ ├── whoops.md │ ├── apollo.md │ └── postgresql.md └── rpc.md ├── .gitignore ├── t.php ├── README.md └── composer.json /.editorconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /en/.keep: -------------------------------------------------------------------------------- 1 | keep 2 | -------------------------------------------------------------------------------- /zh-CN/image/extra/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /zh-CN/notice/index.md: -------------------------------------------------------------------------------- 1 | # 特别注意 -------------------------------------------------------------------------------- /zh-CN/notice/memory-leak.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zh-CN/image/tcp-server/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /zh-CN/best-practices/demo.md: -------------------------------------------------------------------------------- 1 | > TODO -------------------------------------------------------------------------------- /zh-CN/best-practices/deploy.md: -------------------------------------------------------------------------------- 1 | > TODO -------------------------------------------------------------------------------- /zh-CN/notice/performance-optimization.md: -------------------------------------------------------------------------------- 1 | # 性能调优 -------------------------------------------------------------------------------- /zh-CN/annotation/why.md: -------------------------------------------------------------------------------- 1 | Swoft 框架使用注解最大的原因就是因为简洁、灵活。 -------------------------------------------------------------------------------- /zh-CN/rpc-server/request-response.md: -------------------------------------------------------------------------------- 1 | # 请求与响应 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .git/ 3 | vendor/ 4 | .DS_Store 5 | composer.lock -------------------------------------------------------------------------------- /zh-CN/common/stdlib.md: -------------------------------------------------------------------------------- 1 | # 基础帮助库 2 | 3 | ## String 4 | 5 | ## Array 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /zh-CN/image/wx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/wx.png -------------------------------------------------------------------------------- /zh-CN/image/aop/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/aop/1.jpg -------------------------------------------------------------------------------- /zh-CN/image/aop/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/aop/2.jpg -------------------------------------------------------------------------------- /zh-CN/image/aop/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/aop/3.jpg -------------------------------------------------------------------------------- /zh-CN/best-practices/index.md: -------------------------------------------------------------------------------- 1 | # 最佳实践 2 | 3 | 本章主要介绍传统云原生应用(微服务)架构的一些基本组件的原理与实践方案。 4 | 5 | 6 | -------------------------------------------------------------------------------- /zh-CN/image/aop/AOP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/aop/AOP.png -------------------------------------------------------------------------------- /zh-CN/image/aop/OOP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/aop/OOP.png -------------------------------------------------------------------------------- /zh-CN/rpc-client/index.md: -------------------------------------------------------------------------------- 1 | # RPC Client 2 | 3 | 服务调用方法,通过使用服务提供方法,提供的lib接口,调用接口实现服务,不需要了解实现细节。 4 | -------------------------------------------------------------------------------- /zh-CN/image/ready/fpm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ready/fpm.png -------------------------------------------------------------------------------- /zh-CN/image/ready/lnmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ready/lnmp.png -------------------------------------------------------------------------------- /zh-CN/image/bean/DIPUML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/bean/DIPUML.png -------------------------------------------------------------------------------- /zh-CN/image/extra/block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/extra/block.png -------------------------------------------------------------------------------- /zh-CN/image/extra/memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/extra/memory.png -------------------------------------------------------------------------------- /zh-CN/image/extra/trace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/extra/trace.png -------------------------------------------------------------------------------- /zh-CN/image/rpc-server/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/rpc-server/1.png -------------------------------------------------------------------------------- /zh-CN/image/rpc-server/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/rpc-server/2.png -------------------------------------------------------------------------------- /zh-CN/image/rpc-server/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/rpc-server/3.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrate.png -------------------------------------------------------------------------------- /zh-CN/image/bean/NODIPUML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/bean/NODIPUML.png -------------------------------------------------------------------------------- /zh-CN/image/console/cli-app.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/console/cli-app.jpg -------------------------------------------------------------------------------- /zh-CN/image/extra/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/extra/monitor.png -------------------------------------------------------------------------------- /zh-CN/image/extra/process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/extra/process.png -------------------------------------------------------------------------------- /zh-CN/image/ms/breaker_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ms/breaker_base.png -------------------------------------------------------------------------------- /zh-CN/image/ms/breaker_ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ms/breaker_ext.png -------------------------------------------------------------------------------- /zh-CN/image/ready/eventloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ready/eventloop.png -------------------------------------------------------------------------------- /zh-CN/image/tool/UpConfirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/UpConfirm.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateAdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateAdd.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateHis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateHis.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateUp.png -------------------------------------------------------------------------------- /zh-CN/image/best/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/best/architecture.png -------------------------------------------------------------------------------- /zh-CN/image/console/cli-group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/console/cli-group.jpg -------------------------------------------------------------------------------- /zh-CN/image/ms/limiter_leaky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ms/limiter_leaky.png -------------------------------------------------------------------------------- /zh-CN/image/ms/limiter_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/ms/limiter_token.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateUpDb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateUpDb.png -------------------------------------------------------------------------------- /zh-CN/container/index.md: -------------------------------------------------------------------------------- 1 | # 容器 2 | 3 | Swoft 服务容器是一个用于管理类依赖和执行依赖注入的强大工具。依赖注入听上去很花哨,其实质是通过构造函数或者某些情况下通过 setter 方法将类依赖注入到类中。 -------------------------------------------------------------------------------- /zh-CN/image/console/cli-command.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/console/cli-command.jpg -------------------------------------------------------------------------------- /zh-CN/image/console/cli-version.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/console/cli-version.jpg -------------------------------------------------------------------------------- /zh-CN/image/extra/swoft-whoops.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/extra/swoft-whoops.jpg -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateCreate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateCreate.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateMessge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateMessge.png -------------------------------------------------------------------------------- /zh-CN/image/tool/migrateUpPrefix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/migrateUpPrefix.png -------------------------------------------------------------------------------- /zh-CN/image/http-server/process/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/http-server/process/1.png -------------------------------------------------------------------------------- /zh-CN/common/index.md: -------------------------------------------------------------------------------- 1 | # 公共函数 2 | 3 | 本章会详细介绍框架内置封装的一些函数,开发者在实际业务中可以直接调用,不用再去封装。 4 | 5 | - 协程函数 6 | - 字符串函数 7 | - 数组函数 8 | - 验证函数 -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/pack-phar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/pack-phar.jpg -------------------------------------------------------------------------------- /zh-CN/image/tcp-server/devtool-tcp-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tcp-server/devtool-tcp-test.png -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-gen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-gen.png -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-home.png -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-new.png -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/hot-restart-help.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/hot-restart-help.jpg -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/run-hot-restart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/run-hot-restart.jpg -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-new-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-new-app.png -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-new-cpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-new-cpt.png -------------------------------------------------------------------------------- /zh-CN/annotation/tool.md: -------------------------------------------------------------------------------- 1 | PHP 使用注解需要一个插件,否则非常不友好。 2 | 3 | ## PhpStorm 4 | 5 | 安装 PHP annotations 插件,可以像写代码一样,写功能注解。它提供强大的智能提示,错误提示功能。 6 | 7 | 8 | -------------------------------------------------------------------------------- /zh-CN/image/http-server/swoft2-request-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/http-server/swoft2-request-flow.png -------------------------------------------------------------------------------- /zh-CN/validator/index.md: -------------------------------------------------------------------------------- 1 | # 验证器 2 | 3 | 正常情况都需要对用户输入参数进行校验,此时就会用到验证器。验证器可以验证控制器中参数,也支持验证 Websocket 参数以及 RPC 参数验证,提供默认和自定义两种类型的验证器,还可添加自定义验证规则。 4 | -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-new-app-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-new-app-example.png -------------------------------------------------------------------------------- /zh-CN/image/tool/swoftcli/swoftcli-new-cpt-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/swoft-doc/2.x/zh-CN/image/tool/swoftcli/swoftcli-new-cpt-example.png -------------------------------------------------------------------------------- /zh-CN/notice/prohibited.md: -------------------------------------------------------------------------------- 1 | # 严禁使用 2 | * 禁止die()、exit()函数 3 | * 禁止使用$_GET、$_POST、$GLOBALS、$_SERVER、$_FILES、$_COOKIE、$_SESSION、$_REQUEST、$_ENV等超全局变量 4 | * 谨慎使用global、static关键字 -------------------------------------------------------------------------------- /zh-CN/ms/govern/index.md: -------------------------------------------------------------------------------- 1 | 微服务,我们建议使用 Service mesh 方式,比如 Istio/Envoy 框架,业务和服务治理分开,但是 Swoft 也为开发者快速构建微服务提供了一套组件。 2 | 3 | - 服务注册与发现 4 | - 熔断器 5 | - 限流 6 | - 配置中心 7 | 8 | 9 | > Swoft 版本必须 2.0.3+ -------------------------------------------------------------------------------- /zh-CN/log/index.md: -------------------------------------------------------------------------------- 1 | # 日志 2 | 3 | 框架日志由控制台日志和应用日志组成,控制台日志一般用于调试打印,应用日志用于记录开发者的业务日志和框架运行日志。 4 | 无论是控制台日志还是应用日志都提供了很多灵活的参数,方便开发者。 5 | 6 | > swoft/log 基于著名的php日志库monolog扩展而来。你可以很方便的添加自定义handler或processor 7 | -------------------------------------------------------------------------------- /zh-CN/best-practices/middleware.md: -------------------------------------------------------------------------------- 1 | # 开源中间件推荐 2 | ## Istio 3 | 现代云原生ServiceMesh技术栈,详见:https://istio.io/zh/ 4 | 5 | ## Orange 网关 6 | 一个基于OpenResty / Nginx的HTTP API Gateway,详见:https://github.com/sumory/orange 7 | 8 | 9 | -------------------------------------------------------------------------------- /t.php: -------------------------------------------------------------------------------- 1 | addCommand($tr->name, $tr, $tr->desc); 11 | $app->run(); 12 | -------------------------------------------------------------------------------- /zh-CN/quick-start/development.md: -------------------------------------------------------------------------------- 1 | # 开发准备 2 | 3 | ## PHPStorm 安装Annotations插件 4 | 5 | 通过文本编辑器的环境进行 Swoft 开发,在使用 Swoft 便捷的注解时,仍需要 use 注解相对应的命名空间, 这显然不是一个高效的做法。 6 | 7 | 我们推荐在 `PHPStorm` 环境下,并安装 Jetbrain 自带的 `PHP Annotations` 插件,可提供注解命名空间自动补全,注解属性代码提醒,注解类跳转等非常有助于提升开发效率的功能。 8 | -------------------------------------------------------------------------------- /zh-CN/process/index.md: -------------------------------------------------------------------------------- 1 | # 进程 2 | 3 | Swoft 进程组件包括如下三部分功能: 4 | 5 | - 进程操作 6 | - 自定义用户进程 7 | - 进程池 8 | 9 | > 2.0.4+ 支持 10 | 11 | ## 安装 12 | 13 | 使用进程组件之前,先安装组件 14 | 15 | ``` 16 | composer require swoft/process 17 | ``` 18 | ## 使用 19 | 20 | 安装成功后,不同的功能有不同的使用方式,详解其对应章节。 21 | -------------------------------------------------------------------------------- /zh-CN/task/index.md: -------------------------------------------------------------------------------- 1 | # 任务 2 | 3 | 某些场景对主流程没有依赖,可以直接使用任务来实现类似这些功能。框架为开发者提供了协程和异步两种任务。切记无论是协程任务还是异步任务,任务里面操作都只支持协程,且只能使用框架封装的所有 IO 操作(数据库、缓存...) 4 | 5 | ## 协程任务 6 | 7 | 协程任务投递任务的时候不会阻塞主进程相当于一次协程调用,一般用于需要等待任务结果返回的场景。 8 | 9 | ## 异步任务 10 | 11 | 有些场景主流程并不关心的任务执行的结果,此时就可以使用异步任务。 -------------------------------------------------------------------------------- /zh-CN/redis/index.md: -------------------------------------------------------------------------------- 1 | Redis 是一个开源的,高级键值对存储数据库。由于它包含 字符串 , 哈希 , 列表 , 集合 , 和 有序集合 这些数据类型,所以它通常被称为数据结构服务器。 2 | 3 | Swoft Redis 默认驱动是 `phpRedis` 驱动方式,默认的需要具备 `phpRedis` 扩展,你可以通过 PECL 安装 `PhpRedis` PHP 扩展。这个扩展安装起来比较复杂,但对于大量使用 `Redis` 的应用程序来说可能会产生更好的性能。 4 | 5 | 如果你需要使用 `predis/predis` 你也可以的,参考`redis 配置`章节 -------------------------------------------------------------------------------- /zh-CN/db/index.md: -------------------------------------------------------------------------------- 1 | # 数据库 2 | 3 | Swoft 2.x DB操作的方法 高度兼容 `Laravel` ,让上手更变得简单, 4 | 5 | ## 事务 6 | 7 | 开始事务后,之间的所有操作都在同一个事务里面,但是不支持并发操作,因为是同一个连接。 8 | 9 | ## 查询构造器 10 | 11 | 查询器是一套封装面向对象的方法,来实现SQL拼装和操作。目前仅实现了 `MySQL` 客户端 采用原生 `PDO` 方式, 12 | 其他类型的数据库客户端可以自己实现,参考配置章节。 13 | 14 | ## 实体 15 | 16 | 实体只有一点小的改动,参考**模型**章节。 17 | 18 | ## SQL语句 19 | 20 | 2.x 没有直接可以获取 之前之后的 `SQL` 可以 调用 `toSql()` 方法查看。 21 | 22 | 或者在 `Swoft\Db\Query\Processor\Processor` 查看正在运行的 sql 23 | -------------------------------------------------------------------------------- /zh-CN/process/process.md: -------------------------------------------------------------------------------- 1 | # 进程 2 | 3 | Swoft 框架中封装了一套经常操作方式,替换 PHP 的 pcntl,PHP自带的pcntl,存在很多不足,如: 4 | 5 | - pcntl没有提供进程间通信的功能 6 | - pcntl不支持重定向标准输入和输出 7 | - pcntl只提供了fork这样原始的接口,容易使用错误 8 | 9 | > Swoft 基于 Swoole 进程操作封装,功能与 Swoole 完全一样,建议开发者使用 Swoft 的封装操作,方便框架一起迭代升级。2.0.4+ 支持且需要安装 [swoft-process](index.md) 组件 10 | 11 | 12 | ## 方法列表 13 | 14 | 所有操作在方法,全部在 `Swoft\Process\Process` 里面 15 | 16 | - __construct 17 | - start 18 | - name 19 | - exec 20 | 21 | ... -------------------------------------------------------------------------------- /zh-CN/component/index.md: -------------------------------------------------------------------------------- 1 | # swoft组件 2 | 3 | swoft 的各项功能都是有不同的组件提供的,比如 `http-server` 提供了http服务器相关功能,`websocket-server` 提供了 websocket 服务器的功能。 4 | 5 | 这一章节我们主要介绍如何开发一个组件。 6 | 7 | ## 编写组件 8 | 9 | swoft 提供了非常方便的自定义和扩展能力,你可以像编写一个普通的 composer 包一样为swoft编写组件。 10 | 通过swoft的事件你基本上可以接入swoft应用运行的任何阶段,得到你需要的任何信息。 11 | 12 | 13 | ## 代码规范 14 | 15 | - 功能描述 16 | - 代码风格遵循 PSR-2 17 | - 遵循 PSR-4 自动加载规范 18 | - 有完善的单元测试 19 | 20 | 当然,这些只是建议和推荐,更好的代码结构和风格能便于其他开发者阅读使用,利于推广你的组件。 21 | -------------------------------------------------------------------------------- /zh-CN/i18n/index.md: -------------------------------------------------------------------------------- 1 | ## 使用场景 2 | 3 | 一般用于根据用户语言,需要输出不同的文案。如果没有国际化的封装,业务里面会有大量的判断,并且业务也不好维护,没法统一管理所有文案。 4 | 5 | ## 安装 6 | 7 | ```bash 8 | composer require swoft/i18n 9 | ``` 10 | 11 | ## Git仓库 12 | 13 | - Github https://github.com/swoft-cloud/swoft-event 14 | 15 | ## 参与贡献 16 | 17 | 欢迎参与贡献,您可以 18 | 19 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 20 | - 修改代码然后发起 PR 21 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 22 | -------------------------------------------------------------------------------- /zh-CN/tool/swoftcli/index.md: -------------------------------------------------------------------------------- 1 | # Swoft CLI 2 | 3 | Swoft CLI 是一个独立的命令行应用包,提供了一些内置的工具方便开发者使用。 4 | 5 | - 将一个swoft应用打包成 phar 包 6 | - 监视用户swoft项目的文件更改并自动重新启动服务器 7 | 8 | 后续会根据用户需要增加更多的帮助工具,欢迎用户提供意见和贡献代码 9 | 10 | > swoft-cli 是基于 swoft 2.0 构建的应用,运行使用同样需要swoole 11 | 12 | ## 运行预览 13 | 14 | ![swoft-cli-home](../../image/tool/swoftcli/swoftcli-home.png) 15 | 16 | ## 仓库 17 | 18 | - github https://github.com/swoft-cloud/swoft-cli 19 | 20 | ## 参与贡献 21 | 22 | 您可以 fork 仓库修改然后发起 PR 23 | -------------------------------------------------------------------------------- /zh-CN/introduction/question.md: -------------------------------------------------------------------------------- 1 | # 常见问题 2 | 3 | ## The HTTP server have been running!(PID: xx) 4 | 5 | 出现这种问题,是因为服务已经启动了。通常两种方式解决,第一种方式,stop 服务。第二种方式,`kill pid`。 6 | 7 | ## Could not scan for classes inside xxx which does not appear to be a file nor a folder 8 | 9 | 出现这种问题是`composer`源的问题,一般会在`创建项目`(`composer create swoft/swoft swoft`)的时候出现,解决办法很简答只需要切换源`全局设置`即可。如:`composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/`。 10 | 11 | 更多源的切换请参阅 [Composer配置](../ready/composer.md) 12 | 13 | -------------------------------------------------------------------------------- /zh-CN/annotation/usage.md: -------------------------------------------------------------------------------- 1 | ## 使用 2 | 注解使用很简单,和使用一个类一样,首先使用 use 关键字引入类,其次使用注解 3 | 4 | ```php 5 | use Swoft\Http\Server\Annotation\Mapping\Controller; 6 | 7 | /** 8 | * 类注释 9 | * 10 | * @since 2.0 11 | * 12 | * @Controller("a") 13 | */ 14 | class A 15 | { 16 | } 17 | ``` 18 | 19 | - 第1行引入注解类,像一个普通类一样 20 | - 第8行使用注解,格式 `@类名`,有些类名里面会有参数,比如此注解的 `"a"` 21 | 22 | ## 原理 23 | 24 | 为什么 PHP 普通的注释会有功能?其实很简单,通过 PHP 反射获取类里面是所有注解(https://www.php.net/manual/zh/book.reflection.php), 其次通过 PHP 组件(https://github.com/doctrine/annotations) 使其实现特殊功能。 25 | -------------------------------------------------------------------------------- /zh-CN/config/index.md: -------------------------------------------------------------------------------- 1 | # 配置组件 2 | 3 | Swoft 配置由两部分组成,env 环境配置和 config 应用配置。 4 | 5 | - `env` 一般配置一些和环境相关的一些参数,比如运行模式、资源地址 6 | - `config` 一般用于配置应用级别的配置以及业务级别的配置 7 | 8 | ## 安装 9 | 10 | ```bash 11 | composer require swoft/config 12 | ``` 13 | 14 | ## Git仓库 15 | 16 | - Github https://github.com/swoft-cloud/swoft-config 17 | 18 | ## 参与贡献 19 | 20 | 欢迎参与贡献,您可以 21 | 22 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 23 | - 修改代码然后发起 PR 24 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 25 | -------------------------------------------------------------------------------- /zh-CN/validator/setting.md: -------------------------------------------------------------------------------- 1 | # 配置 2 | 3 | ## 安装 4 | 5 | 使用验证器前,需要安装验证器组件,安装如下: 6 | 7 | ```php 8 | composer require swoft/validator 9 | ``` 10 | 11 | ## 启用 12 | 13 | 成功安装好验证组件后,接下来需要启用验证器,这里以 Http-server 启用为例,其它一样(app/bean.php) 14 | 15 | ```php 16 | return [ 17 | // ...... 18 | 'httpDispatcher' => [ 19 | // ...... 20 | 'afterMiddlewares' => [ 21 | \Swoft\Http\Server\Middleware\ValidatorMiddleware::class 22 | ] 23 | // ...... 24 | ], 25 | // ...... 26 | ]; 27 | ``` 28 | 29 | > 2.0.5+ 验证器默认没有启动,需要开发者自己开启 -------------------------------------------------------------------------------- /zh-CN/quick-start/index.md: -------------------------------------------------------------------------------- 1 | # 环境搭建 2 | 3 | 接下来的几篇文章主要介绍swoft需要的运行环境搭建,以及一些使用注意 4 | 5 | - 运行swoft需要哪些环境的支持 6 | - 在本机手动运行(只支持Mac Linux) 7 | - 安装必须的包 8 | - 使用docker运行 9 | - 使用官方镜像 10 | - 使用docker-compose 11 | 12 | ## 环境需求 13 | 14 | ### 必须安装的 15 | 16 | - 安装PHP并且版本至少 `>7.1` 17 | - 安装php包管理器 `composer` 18 | - 连接迭代器依赖 `pcre` 库 19 | - 安装php扩展swoole, 并且版本至少 `>4.3.0` 20 | - 其他需要安装和启用的php扩展有:`PDO` `redis` 21 | 22 | ### 有冲突的 23 | 24 | 下面列出一些已知的和swoole有冲突的php扩展,请使用swoft时不要安装或禁用它们: 25 | 26 | - `xdebug` 27 | - `xhprof` 28 | - `blackfire` 29 | - `zend` 30 | - `trace` 31 | - `uopz` 32 | -------------------------------------------------------------------------------- /zh-CN/ready/index.md: -------------------------------------------------------------------------------- 1 | ## 基础准备 2 | 3 | 在使用 `swoft` 之前你需要具备一些服务端编程基础理论,在传统的`php-fpm`开发模式下 你基本不需要担心,资源释放,共享变量等相关的问题。 4 | 5 | 不过你不用担心,在 2.x 版本,你可以像在`php-fpm` 模式下开发一样简单。如果你有用过 `Laravel` 入手就更快了,因为 `DataBase` 、 `Redis` 和在 `Laravel` 下使用基本一致。 6 | 7 | 8 | ## 入门须知 9 | 10 | 在 swoole 常驻内存开发模式中,不要用超全局变量共享数据,你可以使用 `Context` 代替,不要使用 `curl` 扩展,当然 `guzzlehttp` 底层也是 `curl` 驱动的,`curl` 暂不支持协程化,具体原因你可以参考[swoole 官方文档](https://wiki.swoole.com/wiki/page/965.html) 11 | 12 | 你可以使用 `swoft` 自带的 `httpClient` ,它和 `guzzlehttp` 一样方便快捷,还原生支持并发调用。你再也不用担心因为某个`http`请求时间过长从而导致服务器`CPU` 疯涨。 13 | 14 | 不过在接下来 swoole 会支持curl 扩展,相信不会等待太久。 15 | 16 | 当然还有很多细节在文档中有仔细描述,在这就不一一描述了。 17 | 18 | -------------------------------------------------------------------------------- /zh-CN/redis/pipeline.md: -------------------------------------------------------------------------------- 1 | # redis 管道操作 2 | 3 | 如果你需要在一个操作中向服务器发送很多命令,推荐你使用管道命令。 pipeline 方法接收一个带有 Redis 实例的 闭包 。 你可以将所有的命令发送给这个 Redis 实例,它们都会一次过执行完: 4 | 5 | 比如连续设置 10 个key,返回的是可以数组,你可以遍历判断是否全部成功: 6 | ```php 7 | public function testPipeline() 8 | { 9 | $count = 10; 10 | $result = Redis::pipeline(function (\Redis $redis) use ($count) { 11 | for ($i = 0; $i < $count; $i++) { 12 | $redis->set("key:$i", $i); 13 | } 14 | }); 15 | 16 | // \count($result) == $count; 17 | 18 | foreach ($result as $index => $value) { 19 | // $index 第几个 20 | // $value == true 或者 $value == false 21 | } 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /zh-CN/rpc-client/setting.md: -------------------------------------------------------------------------------- 1 | # 配置参数 2 | 3 | ```php 4 | return [ 5 | 'user' => [ 6 | 'class' => ServiceClient::class, 7 | 'host' => '127.0.0.1', 8 | 'port' => 18307, 9 | 'setting' => [ 10 | 'timeout' => 0.5, 11 | 'connect_timeout' => 1.0, 12 | 'write_timeout' => 10.0, 13 | 'read_timeout' => 0.5, 14 | ], 15 | 'packet' => bean('rpcClientPacket') 16 | ], 17 | 'user.pool' => [ 18 | 'class' => ServicePool::class, 19 | 'client' => bean('user') 20 | ], 21 | ]; 22 | ``` 23 | 24 | 25 | 如上定义了一个 `user` 服务,连接池配置参数和其它一样。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swoft Documentation 2 | 3 | Modern High performance AOP and Coroutine PHP Framework, based on Swoole 4 | 5 | - [Github repository (Primary repository)](https://github.com/swoft-cloud/swoft) 6 | - [Gitee repository](https://gitee.com/swoft/swoft) 7 | - [Official website](https://swoft.org/) 8 | - [Documentation website](https://swoft.org/docs) 9 | 10 | ## Chats & Discussion 11 | 12 | - QQ Group (1): **548173319** 13 | - QQ Group (2): **778656850** 14 | - Gitter.im https://gitter.im/swoft-cloud/community 15 | 16 | ## Languages 17 | 18 | - [简体中文](./zh-CN) 19 | - [English](https://en.swoft.org/docs) 20 | - More ... 21 | 22 | ## LICENSE 23 | 24 | **Apache License 2.0** 25 | -------------------------------------------------------------------------------- /zh-CN/tool/devtool/index.md: -------------------------------------------------------------------------------- 1 | # 开发者工具 2 | 3 | swoft 提供了一些内置的工具方便开发者使用。 4 | 5 | ## 安装组件 6 | 7 | 绝大部分工具都放置在组件 `swoft/devtool` 中 8 | 9 | ```bash 10 | composer require swoft/devtool 11 | ``` 12 | 13 | ## CLI帮助命令 14 | 15 | 提供了一写CLI帮助命令,以便用户快速生成一些基本class。生成后的类文件只需稍加调整就可使用 16 | 17 | - 运行环境检查 18 | - 提供database table实体类生成 19 | - 提供基本的class模板文件生成 20 | 21 | ## 方便的web UI 22 | 23 | - server, application 信息显示 24 | - 已注册的路由信息(http,websocket,rpc) 25 | - 简单的webSocket测试工具 26 | - 简单的日志查看(TODO) 27 | - web版的类文件生成(TODO) 28 | - 等等各种关于服务器和应用的信息查看 29 | 30 | ## 更多功能 31 | 32 | 后续会根据用户需要增加更多的帮助工具,欢迎用户提供意见和贡献代码 33 | 34 | ## 仓库 35 | 36 | - github https://github.com/swoft-cloud/swoft-devtool 37 | 38 | ## 参与贡献 39 | 40 | 您可以 fork 仓库修改然后发起 PR 41 | -------------------------------------------------------------------------------- /zh-CN/rpc-server/setting.md: -------------------------------------------------------------------------------- 1 | # 配置参数 2 | 3 | RPC 服务启动有单独启动和集成其它服务(Http/Websocket)两种方式,无论那种方式都首先要在 bean.php 配置RPC。 4 | 5 | ```php 6 | return [ 7 | 'rpcServer' => [ 8 | 'class' => ServiceServer::class, 9 | 'port' => 18308, 10 | ], 11 | ] 12 | ``` 13 | 14 | - port 配置启动端口号 15 | - setting 启动配置参数,对应 `swooleServer->setting` 16 | 17 | Http server 启动中集成 RPC 服务: 18 | 19 | ```php 20 | return [ 21 | 'httpServer' => [ 22 | 'class' => HttpServer::class, 23 | 'port' => 18306, 24 | 'listener' => [ 25 | 'rpc' => bean('rpcServer') 26 | ], 27 | 28 | // ... 29 | ], 30 | ] 31 | ``` 32 | 33 | - listener 单独监听一个RPC服务,且同时可以监听多个 RPC 服务 34 | 35 | > 如果是单独启动,无效其它配置直接可以启动。 -------------------------------------------------------------------------------- /zh-CN/bean/request.md: -------------------------------------------------------------------------------- 1 | # Request Bean 2 | 3 | `scope` 为 `request` 类型的 `bean`, 框架初始化的时候并不会`初始化` 4 | 5 | 而是在 `onRequest` 事件触发后 采用懒加载方式, 6 | 7 | 在框架初始化的时候不会初始化. 8 | 9 | 只有真正调用的时候才会被初始化,在当前请求中保持单例,请求结束后会被自动销毁。作用域始终在于一个请求当中 10 | 11 | ## 使用 12 | 13 | 只能通过获取 `BeanFactory::getRequestBean` 获取 14 | ```php 15 | $obj = BeanFactory::getRequestBean($name, (string) $tid); 16 | ``` 17 | - **name** requestBean 的名称/别名/类名 18 | - **tid** 通常是与`是顶级协程ID`绑定。获取`顶级协程ID`,`Co::tid()`获取当前协程环境 `顶级协程ID`。 19 | 20 | 如果你需要获取当前请求`加载`了那些 `request bean` 。可以使用: 21 | ```php 22 | $pool = BeanFactory::getContainer()->getRequestPool() 23 | ``` 24 | ## 销毁 25 | 26 | 在**所有协程**执行完毕后,在`SwoftEvent::COROUTINE_COMPLETE`事件中, 27 | 会`自动销毁`与`顶级协程ID`绑定的`request bean`。 28 | 29 | 这时 `request bean`的生命周期也就结束了。 30 | -------------------------------------------------------------------------------- /zh-CN/websocket-server/manage.md: -------------------------------------------------------------------------------- 1 | # 启动与管理 2 | 3 | 在项目目录下执行如下命令可以看到websocket server的管理命令. 跟http server的管理命令一致. 4 | 5 | ```bash 6 | $ php bin/swoft ws 7 | Description: 8 | There some commands for manage the webSocket server 9 | 10 | Usage: 11 | ws:{command} [arguments] [options] 12 | 13 | Commands: 14 | start Start the webSocket server 15 | stop Stop the running server 16 | restart Restart the running server 17 | 18 | Options: 19 | -h, --help Show help of the command group or specified command action 20 | ``` 21 | 22 | ## 启动 23 | 24 | - 前台运行 25 | 26 | ```bash 27 | $ php bin/swoft ws:start 28 | ``` 29 | 30 | - 后台运行 31 | 32 | ```bash 33 | $ php bin/swoft ws:start -d 34 | ``` 35 | 36 | ## 使用 37 | 38 | 如果你注册了ws的路由处理模块,现在就可以通过浏览器等ws客户端连接上server了 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /zh-CN/redis/transaction.md: -------------------------------------------------------------------------------- 1 | # Redis 事务操作 2 | 3 | redis 事务不能回滚,但是能保证原子性,用 `lua` 脚本也能实现 redis 事务效果 4 | 5 | 下面是一个事务操作例子,返回数据有点特别,偶数为成功与否,奇数为执行 `key`,下面写了一个结果遍历方法 6 | ```php 7 | $count = 2; 8 | $result = Redis::transaction(function (\Redis $redis) use ($count) { 9 | for ($i = 0; $i < $count; $i++) { 10 | $key = "key:$i"; 11 | $redis->set($key, $i); 12 | $redis->get($key); 13 | } 14 | }); 15 | 16 | /* 17 | $result = array(4) { 18 | [0]=> 19 | bool(true) 20 | [1]=> 21 | int(0) 22 | [2]=> 23 | bool(true) 24 | [3]=> 25 | int(1) 26 | }*/ 27 | 28 | foreach ($result as $index => $value) { 29 | if ($index % 2 == 0) { 30 | // is exec ok ? 31 | if ($value) { 32 | // todo... 33 | } 34 | } 35 | } 36 | 37 | ``` -------------------------------------------------------------------------------- /zh-CN/component/how-to-load.md: -------------------------------------------------------------------------------- 1 | # 组件加载 2 | 3 | ## 加载流程与原理 4 | 5 | 这里介绍一下组件的收集和加载原理,有需要可以做一下了解。 6 | 7 | - swoft 通过 composer 的 `ClassLoader` 对象得到所有的 psr4 加载注册信息 8 | - 找到每个psr4命名空间所对应的目录,查看是否有 swoft 需要的 `AutoLoader.php` 9 | - 允许在启动application时,设置跳过扫描一些确定的命名空间以加快速度。 默认跳过扫描 `'Psr\\', 'PHPUnit\\', 'Symfony\\'` 几个公共的命名空间 10 | - 加载各个组件的 `AutoLoader.php` 文件。通过它的配置 **有目的** 的扫描指定的路径,_避免像 1.0 一样扫描了很多无效的目录_ 11 | - `AutoLoader` 必须实现 `LoaderInterface`, 同时可以选择实现其他几个有用的interface 12 | - 实现 `LoaderInterface` 可以定义要扫描的目录 13 | - 实现 `DefinitionInterface` 可以定义一个数组来配置一些组件内的bean 14 | - 实现 `ComponentInterface` 除了同时支持上面的配置外,还可以自定义 **是否启用** 当前组件以及添加一些组件描述 15 | - 开始解析每个组件的 `AutoLoader`,收集注解信息,bean配置等 16 | - 没有启用的组件,将会跳过解析它的 `AutoLoader::class` 扫描配置 17 | - 同样允许在启动application时,设置 **禁用** 指定的 `AutoLoader::class` 以达到禁止扫描这个组件的目的 18 | 19 | -------------------------------------------------------------------------------- /zh-CN/tcp-server/manage.md: -------------------------------------------------------------------------------- 1 | # 启动与管理 2 | 3 | 在项目目录下执行如下命令可以看到tcp server的管理命令. 跟http server的管理命令一致. 4 | 5 | ```bash 6 | $ php bin/swoft tcp 7 | Description: 8 | There some commands for manage the tcp server 9 | 10 | Usage: 11 | tcp:{command} [arguments] [options] 12 | 13 | Commands: 14 | start Start the tcp server 15 | stop Stop the running server 16 | restart Restart the running server 17 | 18 | Options: 19 | -h, --help Show help of the command group or specified command action 20 | ``` 21 | 22 | ## 启动 23 | 24 | - 前台运行 25 | 26 | ```bash 27 | $ php bin/swoft tcp:start 28 | ``` 29 | 30 | > Notice: tcp server default port is `18309` 31 | 32 | - 后台运行 33 | 34 | ```bash 35 | $ php bin/swoft tcp:start -d 36 | ``` 37 | 38 | - 重新启动 39 | 40 | ```bash 41 | $ php bin/swoft tcp:restart 42 | ``` 43 | -------------------------------------------------------------------------------- /zh-CN/config/env.md: -------------------------------------------------------------------------------- 1 | # 环境配置 2 | 3 | ## 配置方式 4 | 5 | ### 文件配置 6 | 7 | 项目根目录配置一个名称为 .env 文件,采用 KV 格式配置,此文件配置的数据,可以加载到内存里面,供业务使用。 8 | 9 | ```ini 10 | APP_DEBUG = 1 11 | SWOFT_DEBUG = 1 12 | ``` 13 | 14 | ### 系统变量 15 | 16 | 除文件方式配置外,还可以把一些参数配置到系统变量,系统变量的参数也会加载到内存,供业务使用 17 | 18 | 19 | ## 如何使用 20 | 21 | swoft 提供了函数读取以上两种方式配置的数据。 22 | 23 | ```php 24 | env(string $key = null, mixed $default = null): mixed 25 | ``` 26 | 27 | 获取一个环境变量的值或所有环境变量参数 28 | 29 | - 返回环境变量 key 的值, 如果环境变量 key 不存在则返回默认值。 如果省略 key 参数,则所有环境变量都将作为关联数组 array 返回 30 | - default 默认值,可以使任何类型,也可以是一个 闭包 31 | - 返回值默认做了转换。比如配置 `true` 字符串,返回的转换成一个 bool 类型 32 | 33 | ## 特殊规则 34 | 35 | | 配置(string) | 转换类型结果 | 36 | | ------ | ------ | 37 | | `true`/`false`/`(true)`/`(false)` | bool | 38 | |`empty`|string(空字符串)| 39 | |`null`| null| 40 | |`A_B`|如果是存在的常量,转成对应的值| 41 | -------------------------------------------------------------------------------- /zh-CN/component/structure.md: -------------------------------------------------------------------------------- 1 | # 组件结构 2 | 3 | ## 目录结构 4 | 5 | 这是官方推荐的组件结构,当然你完全可以自定义。但有一点要注意的是: 6 | 7 | > Notice: `AutoLoader.php` 是一个组件必须存在的文件,swoft依据它来确定这是一个组件和要扫描哪些目录 8 | 9 | ```text 10 | ├── src/ 11 | │ ├── Annotation/ -------- 组件注解类定义(如果你的组件不需要添加新注解,则无需此目录) 12 | │ │ ├── Mapping/ -------- 注解类定义 13 | │ │ └── Parser/ -------- 注解解析类定义 14 | │ ├── Bean/ ------- 一些具有独立功能的 class bean 15 | │ ├── Concern/ ------- traits/abstract classes 16 | │ ├── Contract/ ------- interface classes 17 | │ ├── Exception/ 18 | │ ├── Helper/ 19 | │ ├── Listener/ ------- 添加事件监听器 20 | │ ├── AutoLoader.php -------- 组件扫描等信息 21 | ├── test/ ------ 单元测试代码目录 22 | │ ├── unit/ 23 | │ ├── testing/ 24 | │ └── bootstrap.php 25 | ├── LICENSE 26 | ├── README.md 27 | ├── composer.json 28 | └── phpunit.xml 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /zh-CN/tcp-server/index.md: -------------------------------------------------------------------------------- 1 | # Tcp 服务器 2 | 3 | 从 swoft `2.0.4` 版本开始,提供经过swoft封装的tcp 服务器实现。在原有swoole server的基础上,封装并细化功能使用。 4 | 5 | > Available: `>= v2.0.4` 6 | 7 | ## 功能特色 8 | 9 | - 基于swoft的注解系统,使用方便快速 10 | - 提供统一的协议设置,同时支持EOF和length两种切包方式 11 | - 完善的数据收发解析,统一的上下文/请求与响应对象封装 12 | - 内置请求调度处理,可以像http一样细致的分发请求数据到不同的方法处理。 13 | - 内置支持多种打包方式(`json` `php` `token`),同时可以自由扩展。 14 | 15 | ## 安装 16 | 17 | ```bash 18 | composer require swoft/tcp-server 19 | ``` 20 | 21 | ## Git仓库 22 | 23 | - tcp 数据协议 https://github.com/swoft-cloud/swoft-tcp 24 | - tcp-server https://github.com/swoft-cloud/swoft-tcp-server 25 | 26 | ## 参与贡献 27 | 28 | 欢迎参与贡献,您可以 29 | 30 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 31 | - 修改代码然后发起 PR 32 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 33 | -------------------------------------------------------------------------------- /zh-CN/http-server/index.md: -------------------------------------------------------------------------------- 1 | # http 服务器 2 | 3 | 基于 \Swoole\Http\Server 实现的协程HTTP服务, 框架层做了很好的封装, 用户按照传统的 MVC 方式编写代码, 就能获得协程带来的超高性能. 4 | 5 | ## 安装 6 | 7 | ```bash 8 | composer require swoft/http-server 9 | ``` 10 | 11 | ## http 生命周期 12 | 13 | ![Http生命周期](../image/http-server/process/1.png) 14 | 15 | 了解请求生命周期, 有利于理解HTTP服务各组件, 编写出更好代码. 16 | 17 | ## 请求处理流程 18 | 19 | ![swoft2-request-flow](../image/http-server/swoft2-request-flow.png) 20 | 21 | ## 功能特色 22 | 23 | - 基于 PSR-7 的 HTTP 消息实现 24 | - 基于 PSR-15 的中间件 25 | - `@Controller` 灵活的控制器注解 26 | - `@RequestMapping` 灵活的路由注解 27 | 28 | ## Git仓库 29 | 30 | - Github https://github.com/swoft-cloud/swoft-http-server 31 | 32 | ## 参与贡献 33 | 34 | 欢迎参与贡献,您可以 35 | 36 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 37 | - 修改代码然后发起 PR 38 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 39 | -------------------------------------------------------------------------------- /zh-CN/common/timer.md: -------------------------------------------------------------------------------- 1 | # 定时器 2 | 3 | 对应一些延迟执行或者周期性执行的任务,此时需要使用定时器实现。Swoft 框架中封装了一套定时器操作方式,开发者可以直接使用,切记一定要使用 Swoft 框架封装的,如果直接使用 Swoole 提供的会因为上下文,带来一系列问题。 4 | 5 |

2.0.5+ 支持,定时器一定要使用 Swoft 框架封装的,不要直接使用 Swoole 提供的。

6 | 7 | ## Tick 8 | 9 | 周期性的执行一项任务,使用例子如下: 10 | 11 | ```php 12 | $paramOne = 1; 13 | $paramTwo = 1; 14 | \Swoft\Timer::tick(1, function ($paramOne, $paramTwo) { 15 | // To to something 16 | }, $paramOne, $paramTwo); 17 | ``` 18 | 19 | `\Swoft\Timer::tick` 详细参数: 20 | 21 | - $msec 周期执行的时间,单位是毫秒 22 | - $callback 回调函数 23 | - $params 传递的参数,会原封不动的传递给回调函数。 24 | 25 | ## After 26 | 27 | 延迟一段时间执行任务,使用例子如下: 28 | 29 | ```php 30 | $paramOne = 1; 31 | $paramTwo = 1; 32 | \Swoft\Timer::after(1, function ($paramOne, $paramTwo) { 33 | // To to something 34 | }, $paramOne, $paramTwo); 35 | ``` 36 | 37 | - $msec 延迟时间,单位是毫秒 38 | - $callback 回调函数 39 | - $params 传递的参数,会原封不动的传递给回调函数。 -------------------------------------------------------------------------------- /zh-CN/bean/singleton.md: -------------------------------------------------------------------------------- 1 | # singleton 2 | 3 | 单例Bean全局只有一个共享的实例,所有将单例Bean作为依赖的情况下,容器返回将是同一个实例。 4 | 5 | 换言之,当开发者定义一个Bean的作用域为单例时,Swoft IOC容器只会根据Bean定义来创建该Bean的唯一实例。这些唯一的实例会缓存到容器中,后续针对单例Bean的请求和引用,都会从这个缓存中拿到这个唯一的实例。 6 | 7 | ## 简介 8 | `singleton`的`bean` 会在框架`启动时`被初始化,并且只会初始化一次。 9 | 对`singleton`的 `bean` 进行写入操作是不安全的。如果同时读写`singleton`的 `bean`,会造成上下文切换导致`bean`数据不一致,这种往往是业务交叉造成的。。 10 | 11 | ## 特别注意 12 | `singleton` 一般用于`只读`, 不要把它当做共享内存的方式。 13 | 14 | 例如 `swoft` 的 `config bean`,就是一个全局单例的。它只用于读取,在运行过程中不会发生改变。系统把`config` 写操作都抛出了异常。 15 | 16 | ## 定义 17 | 18 | ```php 19 | 20 | 注意:需要将下面命令里的 `{VERSION}` 替换为指定的版本。当然也你可以直接通过浏览器下载 10 | 11 | ```bash 12 | wget https://github.com/swoft-cloud/swoft-cli/releases/download/{VERSION}/swoftcli.phar 13 | 14 | # 检查包是否可用 15 | php swoftcli.phar -V 16 | php swoftcli.phar -h 17 | ``` 18 | 19 | ## 全局使用 20 | 21 | 如果你需要在任何地方都可以直接使用 `swoftcli`: 22 | 23 | ```bash 24 | # move to ENV path: 25 | mv swoftcli.phar /usr/local/bin/swoftcli 26 | chmod a+x /usr/local/bin/swoftcli 27 | 28 | # check 29 | swoftcli -V 30 | ``` 31 | 32 | ## 构建 33 | 34 | 如果你需要从最新的代码构建phar包: 35 | 36 | ```bash 37 | git clone https://github.com/swoft-cloud/swoft-cli 38 | cd swoft-cli 39 | composer install 40 | 41 | // build 42 | php -d phar.readonly=0 bin/swoftcli phar:pack -o=swoftcli.phar 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /zh-CN/console/usage.md: -------------------------------------------------------------------------------- 1 | # 使用与运行 2 | 3 | 完成定义命令后,可以执行命令,处理对应业务逻辑 4 | 5 | > 如果不特殊设置,swoft 默认在协程环境运行命令 6 | 7 | ## 特别说明 8 | 9 | command 里是始终不可能直接操作server的。 你每运行一次 command,都是在一个全新的进程里,除了使用一样的代码,其他毫无关系。 10 | 11 | ## 查看命令 12 | 13 | 查看当前已经定义的所有命令组 14 | 15 | ```bash 16 | php bin/swoft 17 | # 或者 18 | php bin/swoft -h 19 | php bin/swoft --help 20 | ``` 21 | 22 | ![](../image/console/cli-app.jpg) 23 | 24 | ## 查看版本 25 | 26 | 查看当前 swoft 框架版本信息 27 | 28 | ```bash 29 | php bin/swoft -V 30 | # 或者 31 | php bin/swoft --version 32 | ``` 33 | 34 | ![](../image/console/cli-version.jpg) 35 | 36 | ## 命令帮助信息 37 | 38 | - 命令组帮助信息 39 | 40 | ```bash 41 | php bin/swoft http 42 | # 或者 43 | php bin/swoft http -h 44 | ``` 45 | 46 | ![](../image/console/cli-group.jpg) 47 | 48 | - 具体命令帮助信息 49 | 50 | ```bash 51 | php bin/swoft http:start -h 52 | # 或者 53 | php bin/swoft http:start --help 54 | ``` 55 | 56 | ![](../image/console/cli-command.jpg) 57 | 58 | ## 执行命令 59 | 60 | 运行命令组下面,某个操作命令。 61 | 62 | ```bash 63 | php bin/swoft http:start 64 | ``` 65 | -------------------------------------------------------------------------------- /zh-CN/aop/order.md: -------------------------------------------------------------------------------- 1 | # 顺序 2 | 3 | 多个切面执行是按照 `order` 值越小越先执行,一个切面多个通知点,也是按照一定的顺序执行的。 4 | 5 | ## 单切面 6 | 7 | 一个切面顺序相对来说很简单,但是分异常和正常两种情况 8 | 9 | ### 正常顺序 10 | 11 | ![正常](../image/aop/1.jpg) 12 | 13 | 1. `@Around` 通知的 before around 业务 14 | 2. `@Before` 通知 15 | 3. method 方法 16 | 4. `@Around` 通知的 after around 业务 17 | 5. `@After` 通知 18 | 6. 执行 `@AfterReturn` 通知 19 | 20 | ### 异常顺序 21 | 22 | ![异常](../image/aop/2.jpg) 23 | 24 | 1. `@Around` 通知的 before around 业务 25 | 2. `@Before` 通知 26 | 3. method 方法 27 | 4. `@Around` 通知的 after around 业务 28 | 5. `@After` 通知 29 | 6. 执行 `@AfterThrowning` 通知 30 | 31 | ## 多切面 32 | 33 | ![多切面](../image/aop/3.jpg) 34 | 35 | 多切面已正常情况为例: 36 | 37 | 1. Aspect1 `@Around` 通知的 before around 业务 38 | 2. Aspect1 `@Before` 通知 39 | 3. Aspect2 `@Around` 通知的 before around 业务 40 | 4. Aspect2 `@Before` 通知 41 | 5. method 方法 42 | 6. Aspect2 `@Around` 通知的 after around 业务 43 | 7. Aspect2 `@After` 通知 44 | 8. Aspect2 执行 `@AfterReturn` 通知 45 | 9. Aspect1 `@Around` 通知的 after around 业务 46 | 10. Aspect1 `@After` 通知 47 | 11. Aspect1 执行 `@AfterReturn` 通知 48 | 49 | 50 | -------------------------------------------------------------------------------- /zh-CN/common/generic.md: -------------------------------------------------------------------------------- 1 | # 通用函数 2 | 3 | ## env 4 | 5 | 获取环境变量的值 6 | 7 | ```php 8 | function env(string $key = null, $default = null): mixed 9 | ``` 10 | 11 | ## alias 12 | 13 | 获取路径别名的值 14 | 15 | ```php 16 | function alias(string $key): string 17 | ``` 18 | 19 | ## bean 20 | 21 | 从容器中获取bean对象,等同于 `\Swoft::getBean()` 22 | 23 | ```php 24 | function bean(string $key): object 25 | ``` 26 | 27 | ## config 28 | 29 | 获取应用配置的值 30 | 31 | ```php 32 | function config(string $key, $default = null): mixed 33 | ``` 34 | 35 | ## sgo 36 | 37 | 开启一个新的协程 38 | 39 | ```php 40 | function sgo(callable $callable, bool $wait = true): void 41 | ``` 42 | 43 | ## server 44 | 45 | ```php 46 | function server(): \Swoft\Server\Server 47 | ``` 48 | 49 | ## context 50 | 51 | 获取上下文对象 52 | 53 | ```php 54 | function context(): ContextInterface 55 | ``` 56 | 57 | ## container 58 | 59 | 获取容器对象 60 | 61 | ```php 62 | function container(): Container 63 | ``` 64 | 65 | ## vdump 66 | 67 | 打印数据,跟 var_dump 一样,只是会在前面多打出调用的位置,方便调试。 68 | 69 | ```php 70 | function vdump(...$vars) 71 | ``` 72 | -------------------------------------------------------------------------------- /zh-CN/http-server/http.md: -------------------------------------------------------------------------------- 1 | Http Client 官方建议使用 saber 和 Guzzle ,不再重复造轮子。 2 | 3 | 4 | ## Saber 5 | 6 | Swoole 官方封装的 Http client 库,已在多个大型项目中使用。 7 | 8 | ### 安装 9 | ``` 10 | composer require swlib/saber 11 | ``` 12 | 13 | ### 使用 14 | 15 | ```php 16 | SaberGM::get('http://httpbin.org/get'); 17 | SaberGM::delete('http://httpbin.org/delete'); 18 | SaberGM::post('http://httpbin.org/post', ['foo' => 'bar']); 19 | SaberGM::put('http://httpbin.org/put', ['foo' => 'bar']); 20 | SaberGM::patch('http://httpbin.org/patch', ['foo' => 'bar']); 21 | ``` 22 | 23 | 24 | ## Guzzle 25 | 26 | Guzzle 老牌 HTTP 封装库,很多依赖都是它封装的。 27 | 28 | > swoole 版本必须 4.4 即以上 29 | 30 | ### 安装 31 | ``` 32 | composer require guzzlehttp/guzzle 33 | ``` 34 | 35 | ### 使用 36 | 37 | ```php 38 | $client = new \GuzzleHttp\Client(); 39 | $response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle'); 40 | 41 | echo $response->getStatusCode(); # 200 42 | echo $response->getHeaderLine('content-type'); # 'application/json; charset=utf8' 43 | echo $response->getBody(); # '{"id": 1420053, "name": "guzzle", ...}' -------------------------------------------------------------------------------- /zh-CN/tool/swoftcli/generate-app-classs.md: -------------------------------------------------------------------------------- 1 | # 生成应用类文件 2 | 3 | 自swoftcli `v0.1.0` 起,支持通过内置命令快速的创建一个应用类。 4 | 5 | 支持创建: 6 | 7 | - console command class 8 | - event listener class 9 | - task class 10 | - crontab task class 11 | - http controller class 12 | - http middleware class 13 | - rpc controller class 14 | - rpc middleware class 15 | - tcp controller class 16 | - websocket module class 17 | - websocket controller class 18 | - and more ... 19 | 20 | ## 查看命令组 21 | 22 | ```bash 23 | php swoftcli.phar gen 24 | // or 25 | php swoftcli.phar gen --help 26 | ``` 27 | 28 | ![swoftcli-gen](../../image/tool/swoftcli/swoftcli-gen.png) 29 | 30 | ## 命令使用 31 | 32 | - 生成http controller 33 | 34 | ```bash 35 | php swoftcli.phar gen:http-ctrl user --prefix /users 36 | ``` 37 | 38 | - 生成http middleware 39 | 40 | ```bash 41 | php swoftcli.phar gen:http-mdl demo 42 | ``` 43 | 44 | - 生成websocket module 45 | 46 | ```bash 47 | php swoftcli.phar gen:ws-mod chat --prefix /chat 48 | ``` 49 | 50 | - 生成websocket controller 51 | 52 | ```bash 53 | php swoftcli.phar gen:ws-ctrl chat 54 | ``` 55 | 56 | -------------------------------------------------------------------------------- /zh-CN/tool/swoftcli/hot-restart.md: -------------------------------------------------------------------------------- 1 | # 自动重启服务 2 | 3 | swoft 2.0 在内置组件中去除了自动重启功能,由新的独立工具 swoft-cli 来提供。 4 | 5 | > 需要注意的是,2.0 里面是重启整个服务而不是像 1.0 一样只reload工作进程 6 | 7 | 命令:`serve:run` (也可以直接使用别名 `run`) 8 | 9 | ## 查看可用选项 10 | 11 | ```bash 12 | php swoftcli.phar run -h 13 | 14 | # 如果已放到了全局PATH里,可以直接这样使用 15 | swoftcli run -h 16 | ``` 17 | 18 | ![hot-restart-help](../../image/tool/swoftcli/hot-restart-help.jpg) 19 | 20 | ### 选项列表 21 | 22 | - `-b, --bin-file` 指定swoft应用的入口文件,默认是 `bin/swoft` 23 | - `--interval` 监控文件的间隔时间,默认 3 秒钟检查一次 24 | - `--php-bin` 指定你的php可执行文件,默认会自动从全局path中寻找php 25 | - `-c, --start-cmd` 指定server启动命令,默认是 `http:start` (启动http server) 26 | - `-w, --watch` 指定要监控的目录,相对于应用目录。默认监控 `app,config` 里的文件变动 27 | 28 | ### 参数列表 29 | 30 | `targetPath` 仅有一个参数,指定要运行的swoft应用所在目录,默认为当前目录 31 | 32 | ## 使用示例 33 | 34 | ```bash 35 | swoftcli run -c ws:start -b bin/swoft /path/to/php/swoft 36 | ``` 37 | 38 | 运行成功后你可以看到如下的信息: 39 | 40 | ![run-hot-restart](../../image/tool/swoftcli/run-hot-restart.jpg) 41 | 42 | 当有文件发生变动时,swoft-cli 就会自动的重新启动应用 43 | 44 | >> 警告⚠️ 使用 swoftcli 监控server开发时,不能将server配置为后台运行,不然swoftcli会错误的认为server意外退出了,导致重复启动。 45 | 46 | -------------------------------------------------------------------------------- /zh-CN/best-practices/architecture.md: -------------------------------------------------------------------------------- 1 | # Swoft微服务架构简图 2 | ![swoft-architecture](../image/best/architecture.png) 3 | 4 | ## 简介 5 | 如上图所示,一个主流的微服务架构会由流量代理层、网关、注册中心、服务层(业务复杂时,为了职责清晰通常会拆为基础服务层和聚合服务两层)、配置中心、数据层等几个主要部分构成。 6 | 7 | ### 代理层 8 | 代理层一般位于流量的入口,可以基于LVS负载均衡、f5负载均衡器、GLB Director、Nginx/OpenResty等中间件来实现。具体细节可以看官方文档,此处不再赘述。 9 | 10 | ### 网关 11 | 通常,我们的系统中的部分聚合服务接口是需要暴露给外界使用的,这就不可避免的会遇到一些通用性的诉求比如:某些接口需要登录后才能调用(认证/鉴权),某些借口又需要限制调用频次(限流),还有一些情况下新老版本的多个接口要同时使用(A/B Test)...这时候怎么办呢?聪明的你肯定想到了,如果在单体应用的情况下,你可以借助Facade模式作为整个系统的门面,对全部请求进行统一的调度或者过滤,这不但可以使得外部调用方负担降低,还可以在一定程度上增强系统的安全性。有很多优秀的通用开源网关实现,如:Kong、Orang,以及目前比较新的Ingress Gateway等等。 12 | 13 | ### 注册中心 14 | 注册中心时整个分布式服务治理的核心所在,它主要用于实现各个服务实例的自动化注册与发现。其实,刚开始进行服务化,服务并不多的时候,几个服务间完全可以在配置中写死对应服务的配置直接调用,但是当服务逐渐增多时这中手工维护的方式就会导致极其难以维护而且更新不及时,这时候我们就需要引入注册中心了。目前主流的开源注册中心的实现方案也有很多,如:Consul、ETCD、Eureka、甚至Zookeeper等等,不同的注册中心在CAP取舍和业务表现上也有所差异有兴趣的同学可以参考这个[对比文档](https://luyiisme.github.io/2017/04/22/spring-cloud-service-discovery-products/)。 15 | 16 | ### 配置中心 17 | 当我们还是一个单体应用时,我们可以把常用的配置项保存在本地配置文件中。但是当我们的服务实例运行在成百上千的节点中的时候,人工维护这些配置的正确的复杂度则会变得很高。这时候我们需要引入配置中心来统一管理所有服务的配置。开源的配置中心实现有很多,如K8S的ConfigMap,携程开源的Apollo、Spring Cloud Config等等。 18 | 19 | ### 数据层 20 | -------------------------------------------------------------------------------- /zh-CN/quick-start/install.md: -------------------------------------------------------------------------------- 1 | # 安装Swoft 2 | 3 | ## Docker方式安装(推荐) 4 | 5 | ```bash 6 | docker run -p 18306:18306 --name swoft swoft/swoft 7 | ``` 8 | 9 | ## Docker-Compose安装(推荐) 10 | 11 | ```bash 12 | git clone https://github.com/swoft-cloud/swoft 13 | cd swoft 14 | docker-compose up 15 | ``` 16 | 17 | ### 提示 18 | 19 | `docker-compose up` 启动后,默认启动了swoft http server,并且它是容器的的第一个进程(`pid=1`),因此进入容器里是 **没法停止/重启server** 的。 20 | 21 | 如果你想在启动后进入容器,手动管理swoft server的启动。需要先稍微调整下 `docker-compose.yml`: 22 | 23 | ```yml 24 | swoft: 25 | image: swoft/swoft 26 | # for local develop 27 | command: php -S 127.0.0.1:13300 28 | ``` 29 | 30 | 添加一个commad命令覆盖默认的启动server命令。启动后,进入容器再手动启动server `php bin/swoft http:start`。 31 | 32 | 此时,server的进程不是容器的第一个进程,就可以使用swoft server的其他命令进行停止/重启操作了。 33 | 34 | ## Composer安装 35 | 36 | ```bash 37 | composer create-project swoft/swoft swoft 38 | ``` 39 | > 通过 `Packagist国内镜像` 加速国内下载速度,请参阅 [Composer配置](../ready/composer.md) 40 | 41 | ## 手动安装 42 | 43 | ```bash 44 | git clone https://github.com/swoft-cloud/swoft 45 | cd swoft 46 | composer install --no-dev # 不安装 dev 依赖会更快一些 47 | cp .env.example .env 48 | vim .env # 根据需要调整启动参数 49 | ``` 50 | 51 | 52 | -------------------------------------------------------------------------------- /zh-CN/ready/composer.md: -------------------------------------------------------------------------------- 1 | # 依赖管理工具 2 | 3 | Composer 作为依赖包管理工具 4 | 5 | > 使用前请确保您已安装PHP 和 [Composer](https://getcomposer.org/download/) 6 | 7 | ## 使用国内源 8 | 9 | - aliyun https://mirrors.aliyun.com/composer/ **推荐** 10 | - tencent https://mirrors.cloud.tencent.com/composer/ 11 | - huaweicloud https://mirrors.huaweicloud.com/repository/php/ 12 | - cnpkg.org https://php.cnpkg.org 13 | - laravel-china https://packagist.laravel-china.org 14 | 15 | ### 方法1:修改全局配置 16 | 17 | 打开终端并执行如下命令: 18 | 19 | ```bash 20 | composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 21 | // OR 22 | composer config -g repo.packagist composer https://mirrors.huaweicloud.com/repository/php/ 23 | ``` 24 | 25 | ### 方法2:修改项目配置 26 | 27 | 打开终端,进入你的项目的根目录(也就是 `composer.json` 文件所在目录),执行如下命令: 28 | 29 | ```bash 30 | composer config repo.packagist composer https://mirrors.aliyun.com/composer/ 31 | // OR 32 | composer config repo.packagist composer https://mirrors.huaweicloud.com/repository/php/ 33 | ``` 34 | 35 | 您也可以手工在composer.json中添加如下内容: 36 | 37 | ```json 38 | "repositories": { 39 | "packagist": { 40 | "type": "composer", 41 | "url": "https://mirrors.aliyun.com/composer/" 42 | } 43 | } 44 | ``` 45 | 46 | ## 相关网址 47 | 48 | - PHP官方地址:http://php.net/ 49 | - Composer官方地址:https://getcomposer.org/ 50 | -------------------------------------------------------------------------------- /zh-CN/bean/prototype.md: -------------------------------------------------------------------------------- 1 | # prototype 2 | 3 | `prototype` 用的是`原型模式`, 它会被框架启动时会被自动初始化. 4 | 5 | ## 原型模式 6 | 7 | 获取 `scope`为`prototype`类型的`bean`每次都是`克隆`初始化好的`bean`。 8 | 9 | clone 一个对象 比 重新`new`一个对象更快, 因为它是拷贝操作。 10 | 11 | 在 `swoft` 中 DB 的`collection` 就是用的`prototype`类型 12 | 13 | ## 如何使用 14 | 15 | 你可以定义一个 `new` 方法,替代`new`关键字 16 | 17 | 比如`Db`使用的`Collection Prototype` : 18 | 19 | > 实体都是 `prototype`,类型的`bean`,所有实体都可以使用`new`方法。 20 | 21 | ```php 22 | items = $items; 57 | 58 | return $self; 59 | } 60 | } 61 | ``` 62 | 63 | 需要引入`PrototypeTrait`,在`PrototypeTrait` 中实现了 `__instance()`方法,该返回的就是一个 clone 的自身对象,你只需对应更新属性值 即可获取一个全新的对象。 64 | -------------------------------------------------------------------------------- /zh-CN/task/statement.md: -------------------------------------------------------------------------------- 1 | # 声明任务 2 | 3 | 使用任务前,必须定义任务,定义任务很简单。如下定一个任务: 4 | 5 | ```php 6 | /** 7 | * Class TestTask 8 | * 9 | * @since 2.0 10 | * 11 | * @Task(name="testTask") 12 | */ 13 | class TestTask 14 | { 15 | /** 16 | * @TaskMapping(name="list") 17 | * 18 | * @param int $id 19 | * @param string $default 20 | * 21 | * @return array 22 | */ 23 | public function getList(int $id, string $default = 'def'): array 24 | { 25 | return [ 26 | 'list' => [1, 3, 3], 27 | 'id' => $id, 28 | 'default' => $default 29 | ]; 30 | } 31 | 32 | /** 33 | * @TaskMapping() 34 | * 35 | * @param int $id 36 | * 37 | * @return bool 38 | */ 39 | public function delete(int $id): bool 40 | { 41 | if ($id > 10) { 42 | return true; 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | ``` 49 | 50 | ## @Task 51 | 52 | 标记类是一个任务 53 | 54 | - name 指定任务名称,默认全路径类名 55 | 56 | 57 | ## @TaskMapping 58 | 59 | 映射名称 60 | 61 | - name 名称映射,默认就类的方法名称 62 | 63 | > ##### 注意 64 | > 1. 被 `@Task` 标记类的每个方法就是一个任务,如果方法没有使用 `@TaskMapping` 注解,不会解析成任务。 65 | > 2. Task投递前,会经过 `Swoft\Task\Packet::pack()` 方法被json_encode,因此,投递entity时,task获得的是entity的数组。同时,投递无法被json_encode的参数会导致报错 66 | (如果是异步任务且没有开启额外的日志,可能效果是task直接结束而没有报错信息)。 -------------------------------------------------------------------------------- /zh-CN/ready/io.md: -------------------------------------------------------------------------------- 1 | # IO 多路复用 2 | 3 | 这的复用指的是复用的`线程`,其实IO复用的历史和多进程一样长。 4 | 5 | ## select/poll 6 | Linux很早就提供了 select 系统调用,可以在一个进程内维持1024个连接。 7 | 8 | 后来又加入了poll系统调用,poll做了一些改进,解决了 1024 限制的问题,可以维持任意数量的连接。 9 | 10 | 但select/poll还有一个问题就是,它需要循环检测连接是否有事件。 11 | 12 | 这样问题就来了,如果服务器有100万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll需要做循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,白白浪费了CPU资源。 13 | 14 | 15 | ## epoll 16 | 直到Linux 2.6内核提供了新的`epoll`系统调用,可以维持无限数量的连接,而且无需轮询,这才真正解决了 `C10K` 问题。现在各种高并发异步IO的服务器程序都是基于`epoll`实现的,比如`Nginx、Node.js、Erlang、Golang`。像 `Node.js`,`Redis` 这样单进程单线程的程序,都可以维持超过1百万TCP连接,全部归功于`epoll`技术。 17 | 18 | 在这就不得不提,基于 `epoll` 实现的 `Reactor` 模型,`IO复用`**异步非阻塞**程序使用经典的`Reactor`模型,`Reactor`顾名思义就是`反应堆`的意思,它本身不处理任何数据收发。只是可以监视一个`socket句柄`的事件变化。 19 | 20 | ![模型](../image/ready/eventloop.png) 21 | 22 | ### 高效的事件处理模式Reactor 模式 23 | 24 | - 主进程/线程往`epoll内核事件`中注册`socket`上的读就绪事件。 25 | 26 | - 主进程/线程调用`epoll_wait`等待`socket`上有数据可读。 27 | 28 | - 当`socket`上有数据可读时,`epoll_wait`通知主进程/线程。主进程/线程则将`socket`可读事件放入请求队列。 29 | 30 | - 睡眠在请求队列上的某个工作线程被唤醒,它从`socket`读取数据,并处理客户请求, 然后往`epoll`内核事件表中注册该`socket`上的写就绪事件。 31 | 32 | - 主线程调用`epoll_wait`等待`socket`可写。 33 | 34 | - 当`socket`可写时,`epoll_wait`通知主进程/线程主进程/线程将`socket`可写事件放入请求队列。 35 | 36 | - 睡眠在请求队列上的某个工作线程被唤醒,它往`socket`上写入服务器处理客户请求 37 | 38 | `swoole` 的 `Reactor` 线程 也是基于 `Reactor` 模型实现的 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swoft/doc", 3 | "type": "project", 4 | "keywords": [ 5 | "php", 6 | "swoole", 7 | "swoft" 8 | ], 9 | "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", 10 | "license": "Apache-2.0", 11 | "require": { 12 | "php": ">7.1", 13 | "ext-pdo": "*", 14 | "ext-json": "*", 15 | "ext-mbstring": "*", 16 | "ext-openssl": "*", 17 | "swoft/stdlib": "^2.0.0", 18 | "toolkit/cli-utils": "^1.0.7", 19 | "erusev/parsedown-extra": "^0.7.1", 20 | "google/cloud-translate": "^1.2" 21 | }, 22 | "require-dev": { 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "Swoft\\DocBuild\\": "build/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "AppTest\\": "./test/" 32 | } 33 | }, 34 | "scripts": { 35 | "trans": "php t.php", 36 | "test": "./vendor/bin/phpunit -c phpunit.xml", 37 | "cs-fix": "./vendor/bin/php-cs-fixer fix $1" 38 | }, 39 | "repositories": { 40 | "packagist": { 41 | "type": "composer", 42 | "url": "https://packagist.laravel-china.org" 43 | }, 44 | "0": { 45 | "type": "git", 46 | "url": "https://github.com/inhere/swoft-comlib.git" 47 | }, 48 | "1": { 49 | "type": "git", 50 | "url": "https://github.com/inhere/google-translate-php.git" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /zh-CN/tool/devtool/entity.md: -------------------------------------------------------------------------------- 1 | # 实体生成 2 | 3 | 实体生成,能快速生成实体文件,`swoft` 中提供了一套简单好用的生成方式.目前官方仅支持 mysql实体生成,其他类型的数据库参照官方例子 4 | 5 | ## 安装 6 | 7 | 使用 `composer` 安装 `composer require swoft/devtool` 8 | 9 | ## 使用 10 | 11 | `php bin/swoft entity:gen` 12 | 13 | `php bin/swoft entity:c` 14 | 15 | `php bin/swoft entity:create [tableName] [--pool=xx] [--path=xx] [--table_prefix=xx] [--field_prefix=xxx] [--exc=xxx] [--td=xxx] [-y]` 16 | 17 | 这三个命名是一个意思. 18 | 19 | 可以使用 `php bin/swoft entity:c -h` 查看 具体命令帮助文档 20 | 21 | - **tableName** 指定生成实体的数据表,如果为空会生成匹配到的所有表实体 22 | 也可以使用 `--table=tableName` 来指定数据表名,多个表用`,`隔开, 23 | 如果设置了数据表前缀,`tableName` 不要带上表前缀. 24 | - **pool** 指定数据库连接池,默认会使用`db.pool`连接池 25 | - **path** 生成实体的路径 支持 `alias` 方式传递路径,默认生成实体路径为`@app/Model/Entity` 26 | 比如: `@app/Model/Entity`中的`@app`会解析到项目的绝对路径. 27 | - **table_prefix** 根据数据表前缀生成,简写是 `tp`,该参数不会受db 中的 参数限制`prefix`底层会通过 `like` 进行表前缀匹配 28 | - **field_prefix** 去掉字段名前缀,简写是`fp`, 29 | 例如 数据库字段名`t_name`,生成实体的时候 加上`--fp=t_`,生成之后的实体属性名为`name`. 30 | - **exc** 不期望生成的数据表名,使用场景例如你需要生成全部的实体部分表不希望生成实体就可以使用它 31 | 多个表用`,`隔开 32 | - **td** 指定生成模板的路径 支持 `alias`方式传递路径,默认使用的模板路径是`'@devtool/devtool/resource/template'` 33 | - **y** 生成文件是否需要确认, `-y` 参数加上就会直接生成文件不会确认提示 34 | 35 | ## 特别注意 36 | 37 | 如果 连接池中的 `db`配置中存在`prefix`表前缀配置,生成的实体名会自动去掉表前缀, 其中 `[tableName]`,`--table`,`[--exc=xxx]` 这两个参数查询`数据表`的时候回自动会带上表前缀. 38 | 39 | > 请先检查 `db`的`charset` 参数是否配置,不然生成实体中文注释将会乱码. -------------------------------------------------------------------------------- /zh-CN/i18n/setting.md: -------------------------------------------------------------------------------- 1 | ## 配置参数 2 | 使用 i18n 功能,一共需要三步 3 | 4 | - 资源文件夹中导入语言文本 5 | - 配置文件中配置相关参数 6 | - 调用相关方法完成文本转换 7 | 8 | 9 | ### 1.导入文本 10 | 我们在 resource/language/ 文件夹下可创建语言组文件夹与文本文件,一个语言组中可以有多个模板文件,如下所示 11 | ``` shell 12 | # resource/language 13 | |-- zh 14 | |-- default.php 15 | |-- msg.php 16 | `-- en 17 | |-- default.php 18 | |-- msg.php 19 | ``` 20 | 21 | 而文本格式是由关联数组组成. 22 | 键值为文本,我们可以在其中插入参数,格式大致如下: 23 | ``` php 24 | // ../en/default.php 25 | return [ 26 | // 文本中可用大括号注入参数 27 | 'sayhello' => 'Hey {name}!', 28 | 'saygoodbye' => 'Bye!', 29 | ]; 30 | 31 | // ../en/msg.php 32 | return [ 33 | 'sayhello' => "Wath's up! {name}", 34 | 'saygoodbye' => 'See you tomorrow!', 35 | ]; 36 | 37 | // ../zh/default.php 38 | return [ 39 | 'sayhello' => "早上好,{name}", 40 | 'saygoodbye' => '再见', 41 | ]; 42 | // ../zh/msg.php 43 | return [ 44 | 'sayhello' => "晚上好,{name}", 45 | 'saygoodbye' => '明天见', 46 | ]; 47 | ``` 48 | ### 2.相关配置 49 | 50 | i18n相关功能配置非常简单, 51 | 只需要在 app/bean.php 配置文件中,配置以下参数,即可开启国际化功能.参数描述见注释 52 | ``` php 53 | return [ 54 | // .... 其他配置 55 | 56 | 'i18n' => [ 57 | // 设置到文本资源目录 58 | 'resoucePath' => '@resource/language/', // 结尾斜线必须 59 | 60 | // 设置默认文本文件夹名称 61 | // 未填写则默认 en 文件夹 62 | 'defaultLanguage' => 'en', 63 | 64 | // 设置默认文本文件名称 65 | // 未填写则默认 default.php 66 | 'defualtCategory' => 'default', 67 | ], 68 | 69 | // .... 其他配置 70 | ]; 71 | ``` 72 | 至此相关参数已配置完毕,下面就可以使用该功能了. 73 | -------------------------------------------------------------------------------- /zh-CN/error/index.md: -------------------------------------------------------------------------------- 1 | # 错误处理 2 | 3 | swoft 提供了完善的异常与错误处理机制,与通常fpm下情况不同,swoft里将错误分为了不同的场景类型。 4 | 5 | ## 安装 6 | 7 | ```bash 8 | composer require swoft/error 9 | ``` 10 | 11 | ## 错误场景 12 | 13 | 场景类型的划分:主要是根据swoole的回调事件范围来划分。 14 | 15 | > 在不同的场景里,即使是同一个地方抛出的异常或错误,处理方式可能也是不同的。 16 | 17 | 比如:db里抛出了一个 `DbException`,在 http 运行场景里你需要处理后返回一个 Reponse 对象。而在cli等环境里,你可以只处理无需返回任何结果 18 | 19 | ## 错误场景类型 20 | 21 | 在 `Swoft\Error\ErrorType` 定义了swoft划分的场景类型。 22 | 23 | ```php 24 | `swoft/console` 基于 [inhere/php-console](https://github.com/inhere/php-console) 改进,并参考了 [symfony/console](https://github.com/symfony/console) 部分特性 29 | 30 | ## Git仓库 31 | 32 | - Github https://github.com/swoft-cloud/swoft-console 33 | 34 | ## 参与贡献 35 | 36 | 欢迎参与贡献,您可以 37 | 38 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 39 | - 修改代码然后发起 PR 40 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 41 | -------------------------------------------------------------------------------- /zh-CN/annotation/index.md: -------------------------------------------------------------------------------- 1 | # 注解 2 | 3 | ## 安装 4 | 5 | ```bash 6 | composer require swoft/annotation 7 | ``` 8 | 9 | ## 什么是注解 10 | 11 | 注解其实就 PHP 代码里面的注释,但是它是有功能含义的。 12 | 13 | ```php 14 | use Swoft\Http\Server\Annotation\Mapping\Controller; 15 | use Swoft\Http\Server\Annotation\Mapping\RequestMapping; 16 | use Swoft\Bean\Annotation\Mapping\Inject; 17 | 18 | /** 19 | * 类注释 20 | * 21 | * @since 2.0 22 | * 23 | * @Controller("a") 24 | */ 25 | class A 26 | { 27 | /** 28 | * 属性注释 29 | * 30 | * @Inject() 31 | * 32 | * @var string 33 | */ 34 | private $pro = ''; 35 | 36 | /** 37 | * @RequestMapping() 38 | * 39 | * 方法注释 40 | */ 41 | public function method(): void 42 | { 43 | 44 | } 45 | } 46 | ``` 47 | 48 | - `@Controller` 注释,定义一个控制器,后续章节详细介绍。 49 | - `@Inject` 注释,定义一个依赖,后续章节详细介绍。 50 | - `@RequestMapping` 注释,定义一个 action,后续章节详细介绍。 51 | 52 |

53 | 严重警告⚠️ 注解必须以 /**开始,并以 */ 结束。否则会导致无法解析! 54 |

55 | 56 | ## 原理 57 | 58 | ## 规范 59 | 60 | PHP 代码里面本身就会有很多注释,开发者写的有功能的注解,如果随意乱写,会导致不好维护。Swoft 框架源码,注解规范如下: 61 | 62 | - 类注解,所有类注释后面 63 | - 属性注解,属性描述之后,其它注释之前 64 | - 方法注解,方法描述之后,其它注释之前 65 | 66 | > Swoft 框架里面的注解规范仅供参考,如果有更好的方式,欢迎讨论。 67 | 68 | ## Git仓库 69 | 70 | - Github https://github.com/swoft-cloud/swoft-annotation 71 | 72 | ## 参与贡献 73 | 74 | 欢迎参与贡献,您可以 75 | 76 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 77 | - 修改代码然后发起 PR 78 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 79 | -------------------------------------------------------------------------------- /zh-CN/http-server/exception.md: -------------------------------------------------------------------------------- 1 | # 异常处理 2 | 3 | 通常我们把异常类放置 `app/Exception` ,异常类处理器放置 `app/Exception/Handler` 异常分为两部分。自定义的 `Exception` 异常类,异常处理类 `ExceptionHandler` 4 | 5 | ## 定义异常类 6 | 7 | 在不同应用场景下,定义不同的异常类,如需要一个控制器抛异常的类 8 | 9 | app/Exception/ControllerException.php 10 | 11 | ```php 12 | namespace App\Exception; 13 | class ApiException extends \Exception 14 | { 15 | 16 | } 17 | ``` 18 | 19 | ## 定义异常处理类 20 | 21 | ```php 22 | namespace App\Exception\Handler; 23 | use App\Exception\ApiException; 24 | use Swoft\Error\Annotation\Mapping\ExceptionHandler; 25 | use Swoft\Http\Message\Response; 26 | use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; 27 | /** 28 | * @ExceptionHandler(ApiException::class) 29 | */ 30 | class ApiExceptionHandler extends AbstractHttpErrorHandler 31 | { 32 | /** 33 | * @param \Throwable $e 34 | * @param Response $response 35 | * @return Response 36 | * @throws \ReflectionException 37 | * @throws \Swoft\Bean\Exception\ContainerException 38 | */ 39 | public function handle(\Throwable $e, Response $response): Response 40 | { 41 | $data = ['code'=>-1,'msg'=>$e->getMessage()]; 42 | return $response->withData($data); 43 | } 44 | } 45 | ``` 46 | 47 | ## 注解 48 | 49 | ### ExceptionHandler 50 | 51 | 异常处理程序,指定这个处理器要处理当异常,当程序抛出 `ExceptionHandler` 注解里有的异常将会自动执行 `handle` 方法 52 | 53 | - 指定异常:参数可以是字符串也可以是数组 54 | 55 | 处理一个异常 56 | ```php 57 | @ExceptionHandler(ApiException::class) 58 | ``` 59 | 60 | 处理多个异常 61 | ```php 62 | @ExceptionHandler({ApiException::class,ServiceException::class}) 63 | ``` 64 | -------------------------------------------------------------------------------- /zh-CN/console/config.md: -------------------------------------------------------------------------------- 1 | # 命令行配置 2 | 3 | ## AutoLoader配置 4 | 5 | ```php 6 | __DIR__, 38 | ]; 39 | } 40 | 41 | /** 42 | * Metadata information for the component 43 | * 44 | * @return array 45 | */ 46 | public function metadata(): array 47 | { 48 | $jsonFile = dirname(__DIR__) . '/composer.json'; 49 | 50 | return ComposerJSON::open($jsonFile)->getMetadata(); 51 | } 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | public function beans(): array 57 | { 58 | return [ 59 | 'cliApp' => [ 60 | 'class' => Application::class, 61 | ], 62 | 'cliRouter' => [ 63 | 'class' => Router::class, 64 | ], 65 | 'cliDispatcher' => [ 66 | 'class' => ConsoleDispatcher::class, 67 | ], 68 | ]; 69 | } 70 | } 71 | ``` 72 | 73 | -------------------------------------------------------------------------------- /zh-CN/validator/anywhere-validator.md: -------------------------------------------------------------------------------- 1 | # 非注解验证器 2 | 3 | 注解方式引用和使用验证器是有限制,只支持在 Http server/ Rpc server /Websocket server 等特定位置使用,在实际业务开发中,其它地方也会涉及参数的验证。 4 | 非注解和注解方式都是引用相同的验证器,一个验证器可以多个位置,多种方式使用,大大的减少了代码的重复成本。 5 | 6 | ## 全局方法 7 | 8 | ```php 9 | function validate(array $data, string $validatorName, array $fields = [], array $userValidators = []): array 10 | ``` 11 | 12 | 全局函数使用,当验证器失败会抛出 `Swoft\Validator\Exception\ValidatorException` 异常 13 | 14 | - $data 需要验证的数据,必须是数组 KV 格式 15 | - $validatorName 使用的验证器( `@Validator()` 注解标记的 ) 16 | - $fields 需要验证的字段,为空验证器所有字段 17 | - $userValidators 同时使用的自定义验证器,支持两种格式。 18 | 19 | ## 使用示例 20 | 21 | 所有参数验证 22 | 23 | ```php 24 | use Swoft\Validator\Annotation\Mapping\Validator; 25 | 26 | $data = [ 27 | 'email' => 'swoft@xx' 28 | ] 29 | 30 | $result = validate($data, Validator::class); 31 | ``` 32 | 33 | 指定字段验证 34 | 35 | ```php 36 | use Swoft\Validator\Annotation\Mapping\Validator; 37 | 38 | $data = [ 39 | 'email' => 'swoft@xx' 40 | ] 41 | 42 | $result = validate($data, Validator::class, ['email']); 43 | ``` 44 | 45 | 同时使用自定义验证器 46 | 47 | ```php 48 | use Swoft\Validator\Annotation\Mapping\Validator; 49 | 50 | $data = [ 51 | 'start' => 12, 52 | 'end' => 16, 53 | ]; 54 | 55 | 56 | $result = validate($data, Validator::class, [], ['testUserValidtor']); 57 | ``` 58 | 59 | 同时使用自定义验证器且传递参数 60 | 61 | ```php 62 | use Swoft\Validator\Annotation\Mapping\Validator; 63 | 64 | $data = [ 65 | 'start' => 12, 66 | 'end' => 16, 67 | 'params' => [1, 2] 68 | ]; 69 | 70 | $users = [ 71 | 'testUserValidtor' => [1, 2] 72 | ]; 73 | 74 | $result = validate($data, Validator::class, [], $users); 75 | ``` 76 | -------------------------------------------------------------------------------- /zh-CN/introduction/swoft.md: -------------------------------------------------------------------------------- 1 | # 关于 Swoft 2 | 3 | Swoft是基于 Swoole 的最流行的企业级PHP应用程序开发框架。有大量开发人员使用Swoft Framework来创建高性能,易于测试和可重用的代码。 4 | 5 | Swoft 开源框架自2018年3月6号发布以来,一直在迭代升级,秉承简单、高效、稳定的宗旨。伴随着 1.x 的开发迭代和 Swoole 4 全协程化,1.x 的底层架构已经不再适合。从2018年11月开始规划 2.x, 底层借鉴 1.x 的经验全部重写,采用 Swoole Hook 方式框架全部协程化,框架整体相比 1.x 更简单,稳定性也提升一个档次。 6 | 7 | Swoft 是一款轻量级的框架,所有组件可以自定义,按需使用。 8 | 9 | Swoft 框架可以用于开发任何 Web 应用程序,构建高性能的 Web 系统、API、中间件、基础服务等等。 10 | 11 | ## Swoft 优势 12 | 13 | 以下是使用Swoft Framework 的很多好处的列表 14 | 15 | - 以组件化方式开发,开发者可以自定义,按需加载使用。 16 | - 框架是一个设计良好的 Web MVC 框架,它为 Web 框架提供了一个很好的选择。 17 | - 为方便 PHP 开发者快速上手,数据库、缓存的使用都高度兼容 Laravel。 18 | - 所有组件都严格加上单元测试和压力测试 19 | 20 | ## 注解 (Annotation) 21 | 22 | Swoft 是首个基于 Swoole 的注解框架,注解思想大量参考 `SpringBoot`, 在它之上进行简化,设计成更适合 phper 开发,使用更简单。 23 | 24 | ## 依赖注入(DI) 25 | 26 | Swoft 最认同的技术是控制反转的依赖注入(DI)。该控制反转(IOC)是一个笼统的概念,它可以在许多不同的方式来表达。依赖注入仅仅是控制反转的一个具体例子。 27 | 28 | 在编写复杂的 PHP 应用程序时,应用程序类应尽可能独立于其他 PHP 类,以增加重用这些类的可能性,并在单元测试时独立于其他类测试它们。依赖注入有助于将这些类粘合在一起,同时保持它们独立。 29 | 30 | 完全依赖注入是什么?我们分别看这两个词。这里依赖部分转化为两个类之间的关联。例如,A类依赖于B类。现在,我们来看第二部分,注入。所有这些意思是,B类将被IoC注入A类。 31 | 32 | 依赖注入可以通过将参数传递给构造函数或通过使用setter方法进行后置构造来实现。由于依赖注入是 Swoft 框架的核心,我们将在相关示例的单独章节中解释这个概念。 33 | 34 | ## 连接池 (Pool) 35 | 36 | 依托于 Swoole 框架自带连接池,提供简单配置就可以使用,无需任何其它成本。 37 | 38 | ## RPC 39 | 40 | 框架封装了一套高性能的 RPC 服务,每个 RPC 调用可以像本地函数一样使用,服务之间通过接口的方式约定规则,完全屏蔽实现细节,大大提升了开发效率,减少了维护成本。 41 | 42 | 43 | ## 面向切面编程(AOP) 44 | 45 | Swoft的关键组件之一是面向切面编程(AOP)框架。跨越应用程序多个点的功能称为横切关注点,这些横切关注点在概念上与应用程序的业务逻辑分离。有各种常见的很好的例子,包括日志记录,声明式事务,安全性,缓存等等。 46 | 47 | OOP中模块化的关键单元是类,而在AOP中,模块化的单元是切面。DI可帮助您将应用程序对象彼此分离,而AOP可帮助您将交叉关注与其影响的对象分离开来。 48 | 49 | Swoft框架的AOP模块提供了一种面向切面的编程实现,允许您定义方法拦截器和切入点,以干净地分离实现应该分离的功能的代码。我们将在单独的章节中更多地讨论Swoft AOP概念。 50 | -------------------------------------------------------------------------------- /zh-CN/rpc-server/command.md: -------------------------------------------------------------------------------- 1 | # RPC server 服务命令 2 | 3 | 在项目根目录执行如下命令 4 | 5 | ```shell 6 | $ php bin/swoft rpc 7 | Usage: 8 | bin/swoft rpc:COMMAND [--opt ...] [arg ...] 9 | 10 | Global Options: 11 | --debug Setting the application runtime debug level(0 - 4) 12 | --no-color Disable color/ANSI for message output 13 | -h, --help Display this help message 14 | -V, --version Show application version information 15 | 16 | Commands: 17 | reload Reload worker processes 18 | restart Restart the http server 19 | start Start the http server 20 | stop Stop the currently running server 21 | 22 | Example: 23 | bin/swoft rpc:start Start the rpc server 24 | bin/swoft rpc:stop Stop the rpc server 25 | 26 | 27 | ``` 28 | 29 | Rpc 的命令都在 `Commands` 30 | 31 | - reload 重新加载 `worker` 进程 32 | - restart 重启 RPC 服务器 33 | - start 启动 RPC 服务器 34 | - stop 停止 RPC 服务器 35 | 36 | ## 使用 37 | 38 | - 前台运行 39 | 40 | ```shell 41 | $ php bin/swoft rpc:start 42 | Information Panel 43 | ********************************************************************** 44 | * RPC | Listen: 0.0.0.0:18307, type: TCP, mode: Process, worker: 1 45 | ********************************************************************** 46 | 47 | RPC server start success ! 48 | ``` 49 | 50 | - 后台运行 51 | 52 | ```shell 53 | $ php bin/swoft rpc:start -d 54 | Information Panel 55 | ********************************************************************** 56 | * RPC | Listen: 0.0.0.0:18307, type: TCP, mode: Process, worker: 1 57 | ********************************************************************** 58 | 59 | RPC server start success ! 60 | 61 | ``` -------------------------------------------------------------------------------- /zh-CN/rpc-client/rpc-1.0.md: -------------------------------------------------------------------------------- 1 | # 1.0 RPC 2 | 3 | 如果系统之前使用的是 Swoft 1.0 RPC server,Swoft 2.0 定义了一种兼容 1.0 RPC 协议,使用很简单。 4 | 5 | > Available: `>= v2.0.3` 6 | 7 | ## 配置 8 | 9 | 使用 2.0 框架中调用 1.0 RPC server 提供的服务,首先必须配置(app/bean.php) 1.0 RPC 协议 10 | 11 | 12 | ```php 13 | return [ 14 | // ... 15 | 'user' => [ 16 | 'class' => ServiceClient::class, 17 | 'host' => '127.0.0.1', 18 | 'port' => 8099, 19 | 'setting' => [ 20 | 'timeout' => 0.5, 21 | 'connect_timeout' => 1.0, 22 | 'write_timeout' => 10.0, 23 | 'read_timeout' => 0.5, 24 | 'package_eof' => "\r\n", 25 | ], 26 | 'packet' => bean('rpcClientSwoftPacketV1') 27 | ], 28 | 29 | // ... 30 | ]; 31 | ``` 32 | 33 | - host/port 配置 1.0 地址和端口即可 34 | - package_eof 必须配置数据包结尾符,1.0 包结尾符是 `\r\n` 35 | - packet 必须配置使用 `bean('rpcClientSwoftPacketV1')` 1.0 打包器 36 | 37 | 38 | ## 使用 39 | 40 | 以上配置完成后,就可以直接使用了。这里直接以调用 Swoft 1.x 的 `App\Lib\DemoInterface` 为例: 41 | 42 | ```php 43 | /** 44 | * Class RpcController 45 | * 46 | * @since 2.0 47 | * 48 | * @Controller() 49 | */ 50 | class RpcController 51 | { 52 | /** 53 | * @Reference(pool="user.pool", version="0") 54 | * 55 | * @var DemoInterface 56 | */ 57 | private $demoServcie; 58 | 59 | /** 60 | * @RequestMapping(route="swoftV1") 61 | * 62 | * @return array 63 | */ 64 | public function swoftV1():array { 65 | return [$this->demoServcie->getUser('1')]; 66 | } 67 | } 68 | ``` 69 | 70 | - 调用 1.x RPC `version` 必须指定,因为 2.x 与 1.0 默认值不一样 71 | - 不能调用 1.x 的 `deferXxxx` 方法 2.0 已经丢弃 72 | - 2.x 里面调用的接口必须和 1.x 接口命名空间、类名以及方法名称参数完全一样。 73 | -------------------------------------------------------------------------------- /zh-CN/component/entry.md: -------------------------------------------------------------------------------- 1 | # 组件入口 2 | 3 | 每个 swoft 组件都必须有一个 组件的自动加载类 `AutoLoader` 4 | 5 | > Notice: 组件的 `Autoloader` 必须继承 `Swoft\SwoftComponent`,才能被swoft正确辨别为组件。 6 | 7 | ## AutoLoader 8 | 9 | ```php 10 | __DIR__, 41 | ]; 42 | } 43 | 44 | /** 45 | * Metadata information for the component 46 | * 47 | * @return array 48 | */ 49 | public function metadata(): array 50 | { 51 | $jsonFile = dirname(__DIR__) . '/composer.json'; 52 | 53 | return ComposerJSON::open($jsonFile)->getMetadata(); 54 | } 55 | 56 | /** 57 | * {@inheritDoc} 58 | */ 59 | public function beans(): array 60 | { 61 | return [ 62 | 'myBean' => [ 63 | 'class' => MySome::class, 64 | ], 65 | ]; 66 | } 67 | } 68 | ``` 69 | 70 | ## 方法说明 71 | 72 | - `enable(): bool` 是否启用这个组件 73 | - `getPrefixDirs(): array` 这个组件你需要扫描那些目录,你 **完全可以指定只扫描一部分目录**。 74 | - `metadata(): array` 列出组件的基本信息。可以直接通过上面的方式,返回 composer.json 里的信息。 75 | - `beans(): array` 如有需要你可配置添加自定义bean 76 | 77 | > Tips: 我们的应用项目 `swoft/swoft` 结构也是类似的,可以看作一个顶级组件,加载配置为 `app/AutoLoader.php`。 78 | -------------------------------------------------------------------------------- /zh-CN/validator/user-validator.md: -------------------------------------------------------------------------------- 1 | # 自定义验证器 2 | 3 | 常见的业务默认验证器就能解决,但是有些业务默认验证器是没法验证,此时就需要用户根据自己业务需求,定义满足自己业务的验证器。 4 | 5 | 6 | ## 声明验证器 7 | 8 | 声明验证器很简单 9 | 10 | - 定一个类实现 `Swoft\Validator\Contract\ValidatorInterface` 接口 11 | - 实现 `validate` 方法,里面可以定义自己的业务验证器逻辑 12 | - 使用 `@Validator` 注解标记这是一个验证器,此注解使用和功能和之前介绍完全一样 13 | 14 | 如下定义了一个验证器,验证 `start` 开始时间不能大于 `end` 结束时间 15 | 16 | ```php 17 | $end) { 49 | throw new ValidatorException('Start cannot be greater than the end time'); 50 | } 51 | return $data; 52 | } 53 | } 54 | ``` 55 | 56 | ### 验证方法详细介绍 57 | 58 | ```php 59 | public function validate(array $data, array $params): array 60 | ``` 61 | - $data 用户输入参数,通过对于的解析器,已解析成数组 62 | - $params 传递给验证器的参数,后续章节详细介绍 63 | 64 | 验证成返回验证处理后的数据 , 如果验证失败,抛出 `Swoft\Validator\Exception\ValidatorException` 异常,其它由框架处理。如果验证器里面需要修改参数值,可以直接修改,修改后获取值的地方会得到新的值。 65 | -------------------------------------------------------------------------------- /zh-CN/task/coroutine.md: -------------------------------------------------------------------------------- 1 | # 协程任务 2 | 3 | 协程任务投递提供了两种方式,单个投递和批量投递,单个投递是在批量投递的基础之上封装的。如下协程任务投递(备注:co/async方法第三个参数传递为数组,当任务声明的参数为数组时,co/async第三个参数需要 `[[xxx,xxx,xxx]]`使用): 4 | 5 | ```php 6 | use Swoft\Task\Task; 7 | 8 | // 协程投递 9 | $data = Task::co('testTask', 'list', [12]); 10 | 11 | // 协程投递 12 | $result = Task::co('testTask', 'delete', [12]); 13 | ``` 14 | 15 | 16 | ## 任务投递 17 | 18 | ```php 19 | Task::co(string $name, string $method, array $params = [], float $timeout = 3, array $ext = []) 20 | ``` 21 | 22 | 单个任务投递,返回数据和任务方法返回的数据完全一致类型也一样 23 | 24 | - name 投递任务任务名称 25 | - method 投递任务的方法名称 26 | - params 任务传递的参数即是任务方法的参数,数组格式传递 27 | - timeout 超时时间,默认 3s 超时 28 | - ext 任务扩展信息,会传递给任务进程里面 29 | 30 | 31 | ```php 32 | // 任务格式 33 | $tasks = [ 34 | [ 35 | 'taskName', 36 | 'method', 37 | ['params'] 38 | ] 39 | ]; 40 | 41 | Task::cos(array $tasks, float $timeout = 3, array $ext = []) 42 | ``` 43 | 44 | - tasks 多个任务集合,格式如上 45 | - timeout 超时时间,默认 3s 超时 46 | - ext 任务扩展信息,会传递给任务进程里面 47 | 48 | ## 任务上下文 49 | 50 | 有些场景需要在任务里面拿到任务的详细信息,这些信息全部在上下文里面。此时可以使用全局函数 `context()` 获取 `Swoft\Task\TaskContext` 上下文对象。上下文提供两个方法,分别获取 `Swoft\Task\Request` 与 `Swoft\Task\Response` 对象,里面包含投递任务的所有信息。 51 | 52 | ```php 53 | $request = context()->getRequest(); 54 | $response = context()->getRespone(); 55 | ``` 56 | 57 | > 注意:一定要在任务里面获取上下文,否则获取的是其它环境的上下文 58 | 59 | ### 任务 Request 60 | 61 | 62 | ```php 63 | namespace Swoft\Task; 64 | 65 | class Request implements RequestInterface 66 | { 67 | // ... 68 | } 69 | ``` 70 | 71 | 方法列表 72 | 73 | - getServer 获取任务 Server 信息 74 | - getTaskId 获取任务 ID,对应 Swoole 任务 ID 75 | - getSrcWorkerId 任务来自的 workerId 76 | - getData 投递任务的原始是数据 77 | - getName 任务名称 78 | - getMethod 任务方法 79 | - getParams 任务参数 80 | - getExt 任务扩展信息 81 | - getExtKey 根据 key 快速获取用户信息 82 | - getType 任务类型 83 | - getTaskUniqid 任务全局唯一ID 84 | -------------------------------------------------------------------------------- /zh-CN/redis/pub-sub.md: -------------------------------------------------------------------------------- 1 | # 发布、订阅 2 | 3 | 4 | ## 发布 publish 5 | 6 | 描述: 将消息发布到频道 7 | 8 | 所需参数 9 | - channel: 要发布到的频道 10 | - message: 消息 11 | 12 | 例子 13 | ```php 14 | Redis::publish('chan-1', 'hello, world!'); // 发送消息 15 | Redis::publish('chan-2', 'hello, world2!'); // 发送消息 16 | ``` 17 | 18 | ## 订阅 subscribe 19 | 20 | 描述: 订阅频道。 21 | 22 | 所需参数 23 | 24 | - channels: 多个通道名称需要是一个数组 25 | - callback: 回调函数接收3个参数。 26 | - redis redis实例 27 | - chan 通道名称 28 | - msg 接受到的消息(mixed) 29 | 30 | 例子: 31 | ```php 32 | function f($redis, $chan, $msg) { 33 | switch($chan) { 34 | case 'chan-1': 35 | ... 36 | break; 37 | 38 | case 'chan-2': 39 | ... 40 | break; 41 | 42 | case 'chan-2': 43 | ... 44 | break; 45 | } 46 | } 47 | // 订阅 3 个 频道 48 | Redis::subscribe(['chan-1', 'chan-2', 'chan-3'], 'f'); 49 | ``` 50 | 51 | ## 按匹配模式订阅频道 pSubscribe 52 | 53 | 使用 psubscribe 方法可以订阅通配符频道,可以用来在所有频道上获取所有消息。 54 | 55 | 参数 56 | - patterns 匹配规则数组 57 | - callback 接受到消息回调的闭包 58 | - redis redis 实例 59 | - pattern 匹配的规则 60 | - chan 通道名称 61 | - msg 接收到的消息(mixed) 62 | 63 | 例子: 64 | ```php 65 | Redis::psubscribe(['*'], function ($redis, $pattern, $chan, $msg) { 66 | echo "Pattern: $pattern\n"; 67 | echo "Channel: $chan\n"; 68 | echo "Payload: $msg\n"; 69 | }); 70 | 71 | Redis::psubscribe(['users.*'], function ($redis, $pattern, $chan, $msg) { 72 | echo "Pattern: $pattern\n"; 73 | echo "Channel: $chan\n"; 74 | echo "Payload: $msg\n"; 75 | }); 76 | ``` 77 | 78 | > 发布订阅使用的 phpredis 原生的 可能过几秒有会断开 socket 连接断开, 在需要在开启发布订阅前 加 79 | 80 | ```php 81 | 82 | ini_set('default_socket_timeout', -1); 83 | 84 | // todo ... 85 | Redis::psubscribe(['users.*'], function ($redis, $pattern, $chan, $msg) { 86 | echo "Pattern: $pattern\n"; 87 | echo "Channel: $chan\n"; 88 | echo "Payload: $msg\n"; 89 | }); 90 | ``` 91 | 92 | 93 | -------------------------------------------------------------------------------- /zh-CN/ready/tradition.md: -------------------------------------------------------------------------------- 1 | # 传统架构 2 | 3 | 在 `Nginx` + `PHP-Fpm` 模式下开发非常简单 不用担心 内存泄露。 4 | 5 | `php`的`fastcgi`进程管理器`php-fpm`和`nginx`的配合已经运行得足够好,但是由于`php-fpm`本身是**同步阻塞进程模型**,在请求结束后释放所有的资源(包括框架初始化创建的一系列对象,导致PHP进程**空转**(创建<-->销毁<-->创建)消耗大量的`CPU`资源,从而导致单机的吞吐能力有限。请求夯住,会导致 `CPU` 不能释放资源, 大大浪费了 `CPU` 使用率。 6 | 7 | ## php-fpm 8 | 9 | php-fpm进程模型也非常简单的,是 属于[预派生子进程模式](#预派生子进程模式) 大家想必知道早期的 Apache 就是采用该模式 来一个请求就 `fork` 一个进程,进程的开销是非常大的。这会大大降低吞吐率,并发数由进程数决定。 10 | ![fpm](../image/ready/fpm.png) 11 | 12 | ### 预派生子进程模式 13 | 14 | 程序启动后就会创建`N`个进程。每个子进程进入`Accept`,等待新的连接进入。当客户端连接到服务器时,其中一个子进程会被唤醒,开始处理客户端请求,并且不再接受新的`TCP连接`。当此连接关闭时,子进程会释放,重新进入`Accept`,参与处理新的连接。 15 | 16 | #### 优势 17 | 18 | 这个模型的优势是完全可以复用进程,不需要太多的上下文切换。 19 | 20 | #### 劣势 21 | 22 | 这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的。 23 | 24 | PHP框架初始化会占用大量的计算资源,每个请求都需要初始化。 25 | 26 | 启动大量进程会带来额外的进程调度消耗。数百个进程时可能进程上下文切换调度消耗占CPU不到1%可以忽略不计,如果启动数千甚至数万个进程,消耗就会直线上升。调度消耗可能占到 CPU 的百分之几十甚至 100%。 27 | 28 | 29 | 如果请求一个第三方请求非常慢,请求过程中会一直占用 `CPU` 资源,浪费了昂贵的硬件资源 30 | 31 | > 例如:即时聊天程序,一台服务器可能要维持数十万的连接,那么就要启动数十万的进程来持。这显然不可能 32 | 33 | 有没有一种技术可以在一个进程内处理所有并发`IO`呢?答案是有,这就是[IO复用](./io.md)技术。 34 | 35 | ## php-fpm工作模式的问题 36 | 37 | ![模型](../image/ready/lnpm.png) 38 | 39 | - `nginx`基于`epoll`事件模型,一个`worker`同时可处理多个请求 40 | 41 | - `fpm-worker`在同一时刻可处理一个请求 42 | 43 | - `fpm-worker`每次处理请求前需要重新初始化`mvc`框架,然后再释放资源 44 | 45 | - 高并发请求时,`fpm-worker`不够用,`nginx`直接响应502 46 | 47 | - fpm-worker进程间切换消耗大 48 | 49 | 50 | ## 那我们还有什么解决方案 ? 51 | 52 | 我们分析我们的业务不难发现,`90%`以上的业务都是**IO密集性业务**,我们只需要提高IO复用的能力就可以提升单机吞吐能力,另外需要将`php-fpm`**同步阻塞模式**替换为**异步非阻塞模式**,异步开启模式比较复杂不易维护,当然不一定使用`php-fpm`,就可以解决我们的核心问题——性能。 53 | 54 | 在 **IO密集性业务** 中我们需要频繁的上下文切换,采用线程模式开发太过复杂。 55 | 56 | 一个进程中能开的线程数也有限,线程太多也会增加 `CPU` 的负荷和内存资源,线程没有**阻塞态**,IO 阻塞也不能主动让出 `CPU`资源,属于**抢占式调度模型**。不太适合 php 开发。 57 | 58 | 在 `swoole 4.+`开启了全协程模式,让同步代码异步执行。详情请看[为什么要使用 swoole](./swoole.md) 59 | 60 | 61 | -------------------------------------------------------------------------------- /zh-CN/task/async.md: -------------------------------------------------------------------------------- 1 | # 异步任务 2 | 3 | 异步任务一般用于不需要结果的场景且异步区执行,不影响主流程。如下异步任务投递: 4 | 5 | ```php 6 | use Swoft\Task\Task; 7 | 8 | $data = Task::async('testTask', 'list', [12]); 9 | 10 | $data = Task::async('testTask', 'delete', [12]); 11 | ``` 12 | 13 | ## 任务投递 14 | 15 | ```php 16 | Task::async(string $name, string $method, array $params = [], array $ext = [], int $dstWorkerId = -1, callable $fallback = null) 17 | ``` 18 | 异步任务投递,返回一个全局唯一的任务ID 19 | 20 | - name 投递任务任务名称 21 | - method 投递任务的方法名称 22 | - params 任务传递的参数即是任务方法的参数,数组格式传递 23 | - ext 任务扩展信息,会传递给任务进程里面 24 | - dstWorkerId 投递的进程 workerId,默认底层按需选择进程 workerId 25 | 26 | ## 任务上下文 27 | 28 | 任务上下文和协程任务章节讲解的完全一样 29 | 30 | ## 异步任务结果 31 | 32 | 有很多情况不需要关注异步任务处理结果,但是也有部分场景需要关注异步任务处理结果,框架为开发者提供了一种事件监听的方式来处理异步任务结果。此事件和普通事件完全一样。如下定义是事件监听: 33 | 34 | ```php 35 | use Swoft\Event\Annotation\Mapping\Listener; 36 | use Swoft\Event\EventHandlerInterface; 37 | use Swoft\Event\EventInterface; 38 | use Swoft\Log\Helper\CLog; 39 | use Swoft\Task\TaskEvent; 40 | 41 | /** 42 | * Class FinishListener 43 | * 44 | * @since 2.0 45 | * 46 | * @Listener(event=TaskEvent::FINISH) 47 | */ 48 | class FinishListener implements EventHandlerInterface 49 | { 50 | /** 51 | * @param EventInterface $event 52 | */ 53 | public function handle(EventInterface $event): void 54 | { 55 | CLog::info(\context()->getTaskUniqid()); 56 | } 57 | } 58 | ``` 59 | 60 | - 事件必须监听 `TaskEvent::FINISH` 61 | - 如果需要获取数据可以从上下文中获取,注意此时获取的是任务完成的上下文对象与任务上下对象不一样。 62 | 63 | ## 异步任务完成上下文 64 | 65 | 在异步任务完成监听器里面可以通过 `content()` 全局函数获取上下文 `Swoft\Task\FinishContext` 对象。 66 | 67 | ```php 68 | $taskData = context()->getTaskData(); 69 | 70 | $taskId = context()->getTaskId(); 71 | 72 | $taskUniqid = context()->getTaskUniqid(); 73 | 74 | $server = context()->getServer(); 75 | ``` 76 | 77 | - getTaskData 任务处理的结果内容 78 | - getTaskId 任务 ID,对应 Swoole 任务 ID 79 | - getTaskUniqid 全局任务唯一ID,框架生成,与任务投递时的全局任务ID一样 80 | - getServer 获取任务 Server 相关信息 -------------------------------------------------------------------------------- /zh-CN/README.md: -------------------------------------------------------------------------------- 1 | # Swoft 2 | 3 | 4 | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) 5 | [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) 6 | [![Docker Build Status](https://img.shields.io/docker/build/swoft/swoft.svg)](https://hub.docker.com/r/swoft/swoft/) 7 | [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) 8 | [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.4.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) 9 | [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) 10 | [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) 11 | [![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) 12 | 13 | ![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/start-http-server.jpg) 14 | 15 | 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 16 | 17 | - 基于 Swoole 扩展 18 | - 内置协程网络服务器(Http/Websocket/RPC/TCP) 19 | - 高性能路由 20 | - 强大的 AOP (面向切面编程) 21 | - 灵活的注解功能 22 | - 全局的依赖注入容器 23 | - 基于 PSR-7 的 HTTP 消息实现 24 | - 基于 PSR-11 的容器规范实现 25 | - 基于 PSR-14 的事件管理器 26 | - 基于 PSR-15 的中间件 27 | - 基于 PSR-16 的缓存设计 28 | - 可扩展的高性能 RPC 29 | - RESTful 支持 30 | - 国际化(i18n)支持 31 | - 快速灵活的参数验证器 32 | - 完善的服务治理,熔断、降级、负载、注册与发现 33 | - 通用连接池 Mysql、Redis、RPC 34 | - 数据库 ORM 35 | - 协程、异步任务投递 36 | - 自定义用户进程 37 | - 强大的日志系统 38 | 39 |

40 | 项目地址 https://github.com/swoft-cloud/swoft 41 |

42 | -------------------------------------------------------------------------------- /zh-CN/event/index.md: -------------------------------------------------------------------------------- 1 | # 事件管理 2 | 3 | Swoft 2 事件进行了更加清晰和严谨的规划。提供了基本的事件注册与触发管理。 4 | 5 | ## 功能说明 6 | 7 | - implement the [Psr 14](https://github.com/php-fig/fig-standards/blob/master/proposed/event-dispatcher.md) - Event dispatcher 8 | - 支持通过注解快速的注册事件,事件组 9 | - 支持设置事件优先级 10 | - 支持对通配符事件的监听 11 | 12 | > 作为Swoft的核心组件,事件管理会自动启用 13 | 14 | ## 安装 15 | 16 | ```bash 17 | composer require swoft/event 18 | ``` 19 | 20 | ## Git仓库 21 | 22 | - Github https://github.com/swoft-cloud/swoft-event 23 | 24 | ## 参与贡献 25 | 26 | 欢迎参与贡献,您可以 27 | 28 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 29 | - 修改代码然后发起 PR 30 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 31 | 32 | ## swoft里的事件 33 | 34 | Swoft 2 事件进行了更加清晰和严谨的规划。提供了丰富的事件,以便于开发者使用。 35 | 36 | 在swoft我们将事件分为三大类: 37 | 38 | - swoole server的回调事件 39 | - swoft server的事件,基于swoole的回调处理,扩展了一些可用事件以增强自定义性 40 | - 应用级别内的自定义事件管理和使用 41 | 42 | ## 相关介绍 43 | 44 | 一些关于自定义事件的拓展介绍说明 45 | 46 | ### 事件分组 47 | 48 | 除了一些特殊的事件外,在一个应用中,大多数事件是有关联的,此时我们就可以对事件进行分组,方便识别和管理使用。 49 | 50 | - **事件分组** 推荐将相关的事件,在名称设计上进行分组 51 | 52 | 例如: 53 | 54 | ```text 55 | swoft.server.* 56 | swoft.process.* 57 | swoft.pool.* 58 | 59 | swoft.http.request.before 60 | swoft.http.request.after 61 | 62 | swoft.db.query.start 63 | swoft.db.query.after 64 | 65 | swoft.redis.start 66 | swoft.redis.after 67 | 68 | swoft.ws.start 69 | swoft.ws.after 70 | 71 | swoft.tcp.start 72 | swoft.tcp.after 73 | 74 | swoft.udp.start 75 | swoft.udp.after 76 | ``` 77 | 78 | ### 事件通配符 `*` 79 | 80 | 支持使用事件通配符 `*` 对一组相关的事件进行监听, 分两种。 81 | 82 | 1. `*` 全局的事件通配符。直接对 `*` 添加监听器(`@Listener("*")`), 此时所有触发的事件都会被此监听器接收到。 83 | 2. `{prefix}.*` 指定分组事件的监听。 84 | - 例如 `@Listener("swoft.db.*")`, 此时所有触发的以 `swoft.db.` 为前缀的事件(eg `swoft.db.query` `swoft.db.connect`)都会被此监听器接收到。 85 | 86 | > 当然,你在事件到达监听器前停止了本次事件的传播`$event->stopPropagation(true)`,就不会被后面的监听器接收到了。 87 | 88 | ### 更多介绍 89 | 90 | 更多关于自定义事件的理解参考 https://github.com/inhere/php-event-manager/blob/master/README.md 91 | -------------------------------------------------------------------------------- /zh-CN/tcp-server/event.md: -------------------------------------------------------------------------------- 1 | # Tcp 事件通知 2 | 3 | 通常情况下,我们无需关心tcp server 相关的 connect close 事件。 4 | 但 swoft 内部都是监听并触发了框架内部定义的相关事件,你同样可以监听并处理一些逻辑。 5 | 6 | ## 事件列表 7 | 8 | ```php 9 | getTarget(); 83 | 84 | var_dump( 85 | $event->getParam(0), // fd 86 | $event->getParam(1), // reactorId 87 | ); 88 | } 89 | } 90 | ``` 91 | 92 | 93 | -------------------------------------------------------------------------------- /zh-CN/i18n/usage.md: -------------------------------------------------------------------------------- 1 | ### 3.实用案例 2 | 语言转换仅仅至于要调用一个简单的方法, 3 | 下面是方法描述: 4 | 5 | ``` php 6 | /** 7 | * @param string $key 文本数组中,对应文本键值 8 | * @param array $params 注入到文本中的参数,以关联数组的形式 9 | [ 10 | 'param1' => 'str1', 11 | 'param2' => 'str2' 12 | ] 13 | 14 | * @param string $locale 资源文件夹下, 分组文件名称. 15 | */ 16 | \Swoft::t(string $key, array $params, string $locale): string 17 | ``` 18 | 19 | 实例演示: 20 | ``` php 21 | 'Hey {name}!', 53 | $res['en'] = \Swoft::t('sayhello', ['name' => 'man']); 54 | 55 | // 更换模板可以使用 '.' 56 | // 显示 language/en/msg.php 模板 57 | $res['en-msg'] = \Swoft::t('msg.sayhello', ['name' => 'man'], 'en'); 58 | 59 | // 显示 language/zh/default.php 模板 60 | $res['zh'] = \Swoft::t('sayhello', ['name' => '李华'], 'zh'); 61 | 62 | // 显示 language/zh/msg.php 模板 63 | $res['zh-msg'] = \Swoft::t('msg.sayhello', ['name' => '李华'], 'zh'); 64 | 65 | return $res; 66 | } 67 | 68 | // ... 69 | } 70 | 71 | ``` 72 | 73 | 结果输出: 74 | 75 | ``` json 76 | { 77 | "en": "Hey man!", 78 | "en-msg": "Wath's up! man", 79 | "zh": "早上好,李华", 80 | "zh-msg": "晚上好,李华" 81 | } 82 | ``` -------------------------------------------------------------------------------- /zh-CN/http-server/command.md: -------------------------------------------------------------------------------- 1 | # Http服务命令 2 | 3 | 在项目根目录执行如下命令 4 | 5 | ```shell 6 | $ php bin/swoft http 7 | Provide some commands to manage the swoft HTTP Server 8 | 9 | Group: http (alias: httpserver,httpServer,http-server) 10 | Usage: 11 | bin/swoft http:COMMAND [--opt ...] [arg ...] 12 | 13 | Global Options: 14 | --debug Setting the application runtime debug level(0 - 4) 15 | --no-color Disable color/ANSI for message output 16 | -h, --help Display this help message 17 | -V, --version Show application version information 18 | 19 | Commands: 20 | reload Reload worker processes 21 | restart Restart the http server 22 | start Start the http server 23 | stop Stop the currently running server 24 | 25 | Example: 26 | bin/swoft http:start Start the http server 27 | bin/swoft http:stop Stop the http server 28 | 29 | View the specified command, please use: bin/swoft http:COMMAND -h 30 | 31 | ``` 32 | 33 | Http的命令都在 `Commands` 34 | 35 | - reload 重新加载 `worker` 进程 36 | - restart 重启 Http 服务器 37 | - start 启动 Http 服务器 38 | - stop 停止 Http 服务器 39 | 40 | ## 使用 41 | 42 | - 前台运行 43 | 44 | ```shell 45 | $ php bin/swoft http:start 46 | Information Panel 47 | *********************************************************************** 48 | * HTTP | Listen: 0.0.0.0:18306, type: TCP, mode: Process, worker: 1 49 | * rpc | Listen: 0.0.0.0:18307, type: TCP 50 | *********************************************************************** 51 | 52 | HTTP server start success ! 53 | ``` 54 | 55 | - 后台运行 56 | 57 | ```shell 58 | $ php bin/swoft http:start -d 59 | Information Panel 60 | *********************************************************************** 61 | * HTTP | Listen: 0.0.0.0:18306, type: TCP, mode: Process, worker: 1 62 | * rpc | Listen: 0.0.0.0:18307, type: TCP 63 | *********************************************************************** 64 | 65 | HTTP server start success ! 66 | ``` 67 | 68 | 在浏览器通过 `http://127.0.0.1:18306/` 访问Http服务 -------------------------------------------------------------------------------- /zh-CN/log/clog.md: -------------------------------------------------------------------------------- 1 | # 控制台日志 2 | 3 | Swoft提供简便的控制台日志使用,便于在开发时打印调试信息。 4 | 5 | ## 配置 6 | 7 | 启动应用里面 (`app\Application.php`) 重写父类方法,可以覆盖配置控制台日志参数。 8 | 9 | ```php 10 | namespace App; 11 | 12 | use Swoft\SwoftApplication; 13 | 14 | /** 15 | * Class Application 16 | * 17 | * @since 2.0 18 | */ 19 | class Application extends SwoftApplication 20 | { 21 | public function getCLoggerConfig(): array 22 | { 23 | return [ 24 | 'name' => 'swoft', 25 | 'enable' => true, 26 | 'output' => true, 27 | 'levels' => 'info,error', 28 | 'logFile' => '' 29 | ]; 30 | } 31 | } 32 | ``` 33 | 34 | ### 选项说明 35 | 36 | - `name` 名称 37 | - `enable` 是否开启 38 | - `output` 是否打印到控制台 39 | - `levels` 输入日志的级别,为空全部输出,具体日志级别配置值,可以引用 `Logger::NOTICE/...` 40 | - `logFile` 控制台日志默认打印到控制台,也可以配置路径,同时写到指定文件 41 | 42 | > swoft 2.0.3 `levels` 修改成字符串,方便开发者覆盖框架默认配置 43 | 44 | ## 使用 45 | 46 | 控制台日志可以直接使用框架提供的 `CLog` 类里面的静态方法操作。 47 | 48 | - 每个日志级别方法都可以传递参数,底层是一个 `sprintf()` 函数封装 49 | - 特殊的:`debug` 日志级别,需要开启 `SWOFT_DEBUG` 才会显示 50 | - 框架内置不同级别不同颜色 51 | 52 | ```php 53 | // debug 54 | CLog::debug('debug'); 55 | 56 | // info 57 | CLog::info('info'); 58 | 59 | // warning 60 | CLog::warning('warning'); 61 | 62 | // error 63 | CLog::error('error'); 64 | 65 | 66 | // 2019/05/12-07:02:57 [DEBUG] Swoft\Processor\ConsoleProcessor:handle(33) debug 67 | // 2019/05/12-07:02:57 [INFO] Swoft\Processor\ConsoleProcessor:handle(33) info 68 | // 2019/05/12-07:02:57 [WARNING] Swoft\Processor\ConsoleProcessor:handle(33) warning 69 | // 2019/05/12-07:02:57 [ERROR] Swoft\Processor\ConsoleProcessor:handle(33) error 70 | ``` 71 | 72 | 73 | ## 关闭信息 74 | 75 | 默认情况下,启动时会打印一些启动信息到控制台。 76 | 77 | > 如果你的 `.env` 开启了 `SWOFT_DEBUG=1` 将会看到更多详细的启动与加载信息。 78 | 79 | 如果你想关闭这些信息,可以在 `app/Application` 添加: 80 | 81 | ```php 82 | 83 | public function getCLoggerConfig(): array 84 | { 85 | $config = parent::getCLoggerConfig(); 86 | // Disable print console log 87 | $config['enable'] = false; 88 | 89 | return $config; 90 | } 91 | ``` 92 | 93 | 修改保存后,重启swoft,可以看到不会有任何信息输出了。 94 | 95 | -------------------------------------------------------------------------------- /zh-CN/db/origin.md: -------------------------------------------------------------------------------- 1 | 2 | # 运行原生的SQL查询 3 | 4 | 5 | # 6 | 一旦配置好数据库连接后,便可以使用 DB 对象运行查询。 DB 为每种类型的查询提供了方法: select,selectOne,update,insert,delete , cursor, statement, affectingStatement 和 unprepared。 7 | 8 | ## 运行查询语句 9 | 10 | ```php 11 | $users = DB::select('select * from `user` where `status` = ?', [1]); 12 | ``` 13 | `select` 方法将始终返回一个数组,数组中的每个结果都是一个 = `Array`,可以像下面这样访问结果值: 14 | 15 | ```php 16 | foreach ($users as $user) { 17 | echo $user->name; 18 | } 19 | ``` 20 | 如果只是查询一条可用 `selectOne` 结果返回是一个 = `Array`, 21 | 如果没有查询到数据返回是一个空的`Array`。 22 | ```php 23 | $sql= 'select * from `user` where `id` = ?'; 24 | $res = DB::selectOne($sql, [1]); 25 | if ($res) { 26 | echo $res->name; 27 | } 28 | ``` 29 | 30 | 如果你最快的方式遍历数据表所有数据 可用使用 `DB::cursor($sql)` 方法 31 | ```php 32 | $sql= 'select * from `user`'; 33 | $res = DB::cursor($sql); 34 | foreach($res as $user){ 35 | echo $user->name; 36 | } 37 | ``` 38 | 为什么说是最快的呢,因为底层采用 `yield` 机制获取数据 比 `chunk` 快很多 39 | 40 | ### 使用命名绑定 41 | 42 | 除了使用 `?` 表示参数绑定外,你也可以使用命名绑定来执行一个查询: 43 | ```php 44 | $results = DB::select('select * from `user` where `id` = :id', ['id' => 1]); 45 | ``` 46 | 47 | ## 运行插入语句 48 | 49 | 可以使用 DB Facade 的 insert 方法来执行 insert 语句。与 select 一样,该方法将原生 SQL 查询作为其第一个参数,并将绑定数据作为第二个参数: 50 | 51 | ```php 52 | DB::insert('insert into users (`id`, `name`) values (?, ?)', [1, 'sumi']); 53 | ``` 54 | 55 | ## 运行更新语句 56 | 57 | update 方法用于更新数据库中现有的记录。该方法返回受该语句影响的行数: 58 | 59 | ```php 60 | $affected = DB::update('update `user` set `status` = 2 where `name` = ?', ['ovo']); 61 | ``` 62 | 63 | ## 运行删除语句 64 | 65 | delete 方法用于从数据库中删除记录。与 update 一样,返回受该语句影响的行数: 66 | 67 | ```php 68 | $deleted = DB::delete('delete from `users`'); 69 | ``` 70 | 71 | ## 运行普通语句 72 | 73 | 有些数据库语句不会有任何返回值。对于这些语句,你可以使用 DB 的 statement 方法来运行: 74 | 75 | ```php 76 | DB::statement('drop table `user`'); 77 | ``` 78 | 79 | 有些数据库语句如果你想知道执行是否有成功。对于这些语句,你可以使用 DB 的 unprepared 方法来运行: 80 | 81 | ```php 82 | $unprepared = DB::unprepared('DROP TRIGGER IF EXISTS `sync_to_item_table`'); 83 | if ($unprepared) { 84 | // Todo something... 85 | } 86 | ``` 87 | 88 | 只有使用 `unprepared` 方法才可以执行多条sql, 需要预处理的 不能执行多条 sql 89 | -------------------------------------------------------------------------------- /zh-CN/ready/swoole.md: -------------------------------------------------------------------------------- 1 | # 为什么要使用 swoole 2 | 3 | `swoole`的强大之处就在于其进程模型的设计,既解决了异步问题,又解决了并行。 4 | 5 | 从4.0版本开始`Swoole`提供了完整的协程(Coroutine)+通道(Channel)特性,带来全的`CSP`编程模型。应用层可使用完全同步的编程方式,底层自动实现异步IO。 6 | 7 | 使用常驻内存模式 可以避免每次框架的初始化,节约了性能开销。 8 | 4.x 底层采用自动化协程转换, 9 | 10 | ### 什么是协程 11 | 12 | Coroutine 是历史比线程的历史还要悠久,协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。Swoole可以为每一个请求创建对应的协程,根据IO的状态来合理的调度协程。 13 | 14 | 开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护 15 | 16 | 同时由于底层封装了协程,所以对比传统的PHP层协程框架,开发者不需要使用`yield`关键词来标识一个协程IO操作,所以不再需要对`yield`的语义进行深入理解以及对每一级的调用都修改为`yield`,这极大的提高了开发效率 17 | 18 | 下面是一个 对比: 19 | 20 | | | 多进程 | 多线程 |协程 | 21 | | ------ | ------ | ------ | ------ | 22 | | 创建 | fork | pthread_create |go | 23 | | 回收 | wait | pthread_join | - | 24 | | 通信方式 | IPC 进程间通 | 数据同步/锁 | array/chan | 25 | | 资源消耗 | 进程切换开销 | 进程切换开销 | 非常低 | 26 | | 并发能力 | 数百 | 数千 | 50万 | 27 | | 编程难度 | 困难 | 非常困难 |容易 | 28 | 29 | ### 协程的优点 30 | 31 | - 用户态线程,遇到 IO 主动让出 32 | - PHP 代码依然是串行执行的,无需加锁 33 | - 开销极低,仅占用内存,不存在进程/线程切换开销 34 | - 并发量大,单个进程可开启 50W 个协程 35 | - 随时随地,只要你想并发,就调用 go 创建新协程 36 | 37 | ### swoole 的协程 38 | 39 | swoole 的协程 和 golang的调度方式完全不同,每一个进程里面的协程都是`串行`执行所以无需担心访问资源加锁问题,这也符合 php 简单的特性。 40 | 41 | > 那么 进程 的协程是`串行` 执行如何利用 多核 `CPU` 实现`并行`呢。答案是利用多进程实现。现在 task 也可以开启协程。 42 | 43 | 这也许没有 golang 性能那么好,但是对于 `IO 密集型业务`是非常适合的,协程的上下文切换非常快。切换一门语言的成本对公司来说也是非常巨大的,现在 swoole 的生态也越来越好。 44 | 45 | 4.0 底层加入 `Hook` 机制,使原生的 `Mysql PDO`、`Redis` 操作协程化,后续将支持 `Curl` 扩展。更贴近传统业务代码,迁移成本也降低。 46 | 47 | `swoole` 的 `http server` 采用优秀的 `Reactor 模型`,处理速度可逼进 `NGINX` 处理静态页面的 速度。对于 `Api`或者`基础服务`来说非常的适合。性能瞬间翻几翻,再也不用担心 php-fpm 进程数过多,导致 `CPU` 被打满。 48 | 49 | ### swoole 需要注意的地方 50 | 51 | 当然它也不是没有缺点的 52 | 53 | 无法做密集计算。当然这一点是php甚至是所有动态语言都存在的问题。写在这里是因为防止误导读者以为使用`swoole`后,`php`可以用来做密集计算。 54 | 55 | 更容易内存泄露。在处理全局变量,静态变量的时候一定要小心,这种不会被GC清理的变量会存在整个生命周期中,如果没有正确的处理,很容易消耗完所有的内存。而以往的php-fpm下,php代码执行完内存就会被完全释放。 56 | 57 | > CSP 有一句很经典的话:不要通过共享内存来通信,而应该通过通信来共享内存 58 | 59 | 这个"通信"你可以理解利用`channel`通信。 60 | 61 | 虽然 swoole 协程是串行,但是业务有可能会**交叉**,比如你一个业务在添加配置一个业务在写配置,上下文一切换,可能会造成配置的不一致,你可能会好奇,本地跑跑着没事到了线上跑一段时间会出问题,这本是业务设计上的问题。 62 | -------------------------------------------------------------------------------- /zh-CN/tool/swoftcli/build-phar.md: -------------------------------------------------------------------------------- 1 | # 打包Phar 2 | 3 | swoft-cli 工具提供了一个简单的命令,可用于将一个php应用(不限于swoft)打包成一个Phar包。 4 | 5 | > 要使用此功能,请确保 `phar` 扩展是启用的。通常情况下,这个扩展在php里是默认启用的 6 | 7 | ## 构建Phar 8 | 9 | ```bash 10 | php -d phar.readonly=0 swoftcli phar:pack -o=myapp.phar 11 | ``` 12 | 13 | 可用选项(_通过帮助查看_): 14 | 15 | - `-c, --config` 指定打包用的配置文件,默认读取当前目录下的 `phar.build.inc` 16 | - `--dir` 要打包的应用目录,默认是当前目录 17 | - `-o, --output` 打包后输出的文件名称,默认是 `app.phar` 18 | 19 | > 注意:运行时必须在前面为php加上选项 `-d phar.readonly=0` 20 | 21 | 运行效果: 22 | 23 | ![pack-phar](../../image/tool/swoftcli/pack-phar.jpg) 24 | 25 | ### phar 打包配置 26 | 27 | phar 打包需要一个配置文件,可以配置一些需要打包的目录、文件等。通常放置于项目根目录下。 28 | 29 | ```php 30 | stripComments(true) 40 | ->setShebang(true) 41 | // 需要打包的文件后缀,内置已经包含了 .php 42 | ->addSuffix(['.tpl', '.json']) 43 | // 排除打包目录,排除一些无用的目录,可以有效减少输出文件大小 44 | ->addExcludeDir([ 45 | 'test', 46 | 'doc', 47 | 'docs', 48 | 'tests', 49 | 'Tests', 50 | 'example', 51 | 'runtime', 52 | 'swoole-ide-helper', 53 | ]) 54 | // 指定添加的文件 55 | ->addFile([ 56 | 'LICENSE', 57 | 'composer.json', 58 | 'README.md', 59 | 'bin/bootstrap.php', 60 | ]) 61 | ->setCliIndex('bin/swoft') // 应用入口 62 | // ->setWebIndex('web/index.php') 63 | // ->setVersionFile('config/config.php') 64 | ; 65 | 66 | // 那些目录需要去除注释。Swoft 相关都不能去除注释 67 | $stripDirs = [ 68 | 'psr/', 69 | 'nikic/', 70 | 'monolog/', 71 | 'symfony/', 72 | 'toolkit/', 73 | ]; 74 | 75 | $compiler->setStripFilter(function ($file) use ($stripDirs) { 76 | /** @var \SplFileInfo $file */ 77 | $path = $file->getPathname(); 78 | 79 | foreach ($stripDirs as $dir) { 80 | if (\strpos($path, $dir)) { 81 | return true; 82 | } 83 | } 84 | 85 | return false; 86 | }); 87 | ``` 88 | 89 | ## 展开Phar包 90 | 91 | 将一个打包过的 phar 展开,类似于解压缩。 92 | 93 | ```bash 94 | swoftcli phar:unpack myapp.phar 95 | ``` 96 | 97 | 98 | -------------------------------------------------------------------------------- /zh-CN/http-server/response.md: -------------------------------------------------------------------------------- 1 | # Http 响应对象 2 | 3 | Swoft 的请求与响应实现于 PSR 7 规范。请求与响应对象存在于每次 HTTP 请求。 4 | 5 | - 请求对象 `Request` 为 `Swoft\Http\Message\Request` 6 | - 响应对象 `Response` 为 `Swoft\Http\Message\Response` 7 | 8 |

注意!
9 | 根据PSR-7对象的不可变性(immutable),所有的 with* 方法都是克隆对象然后返回,必须接收新对象来做进一步处理,或使用链式调用 10 |

11 | 12 | ## 基本方法 13 | 14 | PSR-7 接口为请求和响应对象提供了这些公共方法: 15 | 16 | - `withProtocolVersion($version)` 17 | - `withHeader($name, $value)` 18 | - `withAddedHeader($name, $value)` 19 | - `withoutHeader($name)` 20 | - `withBody(StreamInterface $body)` 21 | 22 | PSR-7 接口为响应对象提供了这些方法: 23 | 24 | - `withStatus($code, $reasonPhrase = '')` 25 | 26 | > 更多请参考 PSR-7 和 查看 [swoft/http-message](https://github.com/swoft-cloud/swoft-http-message) 中具体的实现类 27 | 28 | ## 如何获取 29 | 30 | - 通过控制器方法参数注入 `(Response $response)` 31 | - 通过请求上下文获取 `context()->getResponse()` 32 | - 通过请求上下文获取 `Swoft\Context\Context::mustGet()->getResponse()` (_已废弃_) 33 | 34 | ## 设置状态码 35 | 36 | ```php 37 | $response = \context()->getResponse(); 38 | return $response->withStatus(404); 39 | ``` 40 | 41 | ## 输出字符串内容 42 | 43 | ```php 44 | return $response->withContent("Hello Swoft2.0"); 45 | ``` 46 | 47 | ## 输出数组 48 | 49 | ```php 50 | $data = ['name'=>'Swoft2.0']; 51 | $response->withData($data); 52 | ``` 53 | 54 | ## 输出头信息 55 | 56 | ```php 57 | return $response->withHeader("name","Swoft2.0"); 58 | ``` 59 | 60 | ## 重定向 61 | 62 | ```php 63 | // 302 64 | return $response->redirect("http://www.swoft.org",302); 65 | 66 | // 404 page 67 | return $response->redirect('/404'); 68 | ``` 69 | 70 | ## 文件下载 71 | 72 | ```php 73 | return $response->file(\alias('@runtime/1.zip'), "application/octet-stream"); 74 | ``` 75 | 76 | ## 设置Cookies 77 | 78 | ```php 79 | $response = $response->withCookie(’name', 'value'); 80 | 81 | $response = $response->withCookie(’name', [ 82 | 'value' => 'value3', 83 | 'httpOnly' => true 84 | ]); 85 | ``` 86 | 87 | 设置多个: 88 | 89 | ```php 90 | $cookies = [ 91 | 'key1' => 'value1', 92 | 'key2' => [ 93 | 'value' => 'value2', 94 | ], 95 | 'key3' => [ 96 | 'value' => 'value3', 97 | 'httpOnly' => true 98 | ], 99 | ]; 100 | 101 | $response = $response->withCookies($cookies); 102 | ``` 103 | 104 | -------------------------------------------------------------------------------- /zh-CN/db/transaction.md: -------------------------------------------------------------------------------- 1 | # 事务 2 | 3 | 4 | ## 开启事务 5 | 6 | 开启事务后,事务之间的所有操作都是同一个连接,注意不能使用并发操作。 7 | 8 | 如果你想要开始一个事务,并且对回滚和提交能够完全控制,那么你可以使用 DB 的 `beginTransaction` 方法: 9 | 10 | ```php 11 | DB::beginTransaction(); 12 | ``` 13 | 或者 14 | ```php 15 | DB::connection()->beginTransaction(); 16 | ``` 17 | 一旦开启了事务,当前连接会绑定到当前的协程环境中,保证`提交`和`回滚`,`查询`都是同一个连接保证数据的安全性,只有`提交`或者`回滚`完毕才会解除绑定。 18 | 19 | 20 |

不同连接池的事务, 是相互独立的 . 没有存在关系

21 | 22 | ## 事务回滚 23 | 如果操作失败需要回滚 使用下面两种方式都可以 24 | 25 | ```php 26 | DB::rollBack(); 27 | ``` 28 | 或者 29 | ```php 30 | DB::connection()->rollBack(); 31 | ``` 32 | 33 | ## 事务提交 34 | 如果操作执行成功需要 使用下面两种方式都可以 35 | 36 | ```php 37 | DB::commit(); 38 | ``` 39 | 或者 40 | ```php 41 | DB::connection()->commit(); 42 | ``` 43 | 44 | ## 常见问题 45 | 46 | ### 事务是否支持嵌套 47 | MySQL 官方文档说 如果事务发生嵌套,会隐式提交`上一个事务`,然后开启一个新的事务。 48 | 49 | 框架的答案是可以做`事务嵌套`,因为部分数据库支持 支持 `savepoints` 能力。 50 | 在MySQL中, 保存点`savepoints`属于事务控制处理部分。利用`savepoints`可以回滚指定部分事务,从而使事务处理更加灵活和精细。如果你进行了事务嵌套嵌套的事务会保存在`savepoints`里面,可以做嵌套事务的精细控制。 51 | ```php 52 | DB::beginTransaction(); 53 | $user = User::find($id); 54 | $user->update(['name' => $id]); 55 | 56 | DB::beginTransaction(); 57 | User::find($id)->update(['name'=>'sakuraovq']); 58 | DB::rollBack(); 59 | 60 | DB::commit(); 61 | ``` 62 | `嵌套`里面的的事务进行了`回滚`,不会影响`外层`改变的数据只回滚name=sakuraovq的修改,这段代码执行下来 name = $id 63 | 64 | ### 如果事务没有提交怎么办 65 | ```php 66 | DB::connection()->beginTransaction(); 67 | $user = User::find($id); 68 | 69 | \sgo(function ()use($id) { 70 | DB::connection()->beginTransaction(); 71 | User::find($id); 72 | }); 73 | ``` 74 | 类似这样的代码如果我们忘记 提交事务/回滚。 75 | 76 | Swoft 在`SwoftEvent::COROUTINE_DEFER`事件中会检查是否还处于事务状态,如果是会自动 `rollback`到最初开启事务的状态。连接会归还到连接池中,不会造成资源泄露。 77 | 78 | ## 错误示范 79 | ```php 80 | DB::beginTransaction(); 81 | $user = User::find($id); 82 | 83 | \sgo(function () use ($id) { 84 | $user1 = User::find($id); 85 | }); 86 | $user->update(['name' => 'sakuraovq'.mt_rand(110,10000)]); 87 | DB::commit(); 88 | 89 | ``` 90 | 类似这样的代码虽然执行没有问题,这种写法是错误的,会造成数据的错乱。**请不要在事务中嵌套`协程`,在执行 db 操作**, 上文讲到事务是绑定到`当前协程`的 切换了协程也就是另一个新的连接。 91 | 92 | ```php 93 | DB::connection()->beginTransaction(); 94 | $user = User::find($id); 95 | $user->update(['name' => 2]); 96 | 97 | \sgo(function () use ($user) { 98 | $user->update(['name' => 1]); 99 | }); 100 | DB::rollBack(); 101 | ``` 102 | 这样也是错的,这段代码执行下来你会发现,子协程的修改操作回滚**不受控制**。因为它们使用了不同的连接,并非绑定在一起。 103 | -------------------------------------------------------------------------------- /zh-CN/websocket-server/index.md: -------------------------------------------------------------------------------- 1 | # WebSocket 服务 2 | 3 | WebSocket 服务基于现有 swoole ws server上的进一步封装实现。即开启websocket服务的同时可以处理http请求。 4 | 5 | ## 安装 6 | 7 | ```bash 8 | composer require swoft/websocket-server 9 | ``` 10 | 11 | ## 功能特色 12 | 13 | - 快速的搭建使用 websocket server 14 | - 支持完全的自定义流程处理,如果你不想使用框架自带的处理 15 | - 支持消息阶段的数据解析和路由调度 16 | - 通用的消息发送方法封装(send, sendToSome, sendToAll, broadcast 等) 17 | 18 | ## 连接处理流程 19 | 20 | ```text 21 | 握手请求 -------> 接收到WebSocket请求(根据path找到处理模块 eg EchoModule) 22 | | 23 | |使用模块类中标记的握手方法验证请求 24 | | 25 | v 26 | 握手成功,接受连接 27 | | 28 | | 创建连接上下文Connection,存储到Session管理器 29 | | (含有fd, request等信息) 30 | | 31 | v 32 | 消息请求 --> 接收消息 33 | | 34 | |创建消息上下文Context,存储到Context管理器 35 | |同时通过CoID会与Session的绑定关系 36 | | 37 | V 38 | 解析消息数据 39 | | 40 | |得到消息指令和消息body 41 | |(根据消息指令找到处理控制器 eg ChatController) 42 | | 43 | V 44 | 调度消息处理 45 | | 46 | |调用对应的message控制器方法处理 47 | | 48 | V 49 | 打包返回数据 50 | | 51 | |销毁此次消息请求的上下文Context 52 | |同时删除与Session的绑定关系 53 | | 54 | v 55 | 得到响应 <--- 返回结果 56 | | 57 | 消息请求 --> | 58 | . | 59 | . |(重复上述消息处理流程) 60 | . | 61 | <-- 得到响应 | 62 | | 63 | v 64 | 断开连接 -——-——--> 收到关闭连接事件 65 | | 66 | | 销毁连接上下文,从Session管理器删除此连接 67 | | 68 | v 69 | 关闭连接 70 | ``` 71 | 72 | ## Git仓库 73 | 74 | - github https://github.com/swoft-cloud/swoft-websocket-server 75 | 76 | ## 参与贡献 77 | 78 | 欢迎参与贡献,您可以 79 | 80 | - fork 我们的开发仓库 [swoft/component](https://github.com/swoft-cloud/swoft-component) 81 | - 修改代码然后发起 PR 82 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) -------------------------------------------------------------------------------- /zh-CN/db/selectDb.md: -------------------------------------------------------------------------------- 1 | # 切换数据库 2 | 3 | 在之前的`2.0.2`,之前在一个连接池中没有 提供切库的功能, 导致不同数据库需要配置多个连接池, 这大大增加了维护成本, 所以在 `2.0.2` 提供了切换数据库功能, 你可以在链式操作中手动自定 `db()`方法, 这显然不够灵活难以维护. 4 | 5 | 下面推荐一种根据上下文切库的 操作 6 | 7 | > Available: `>= v2.0.2` 8 | 9 | ## DbSelector 10 | 11 | ```php 12 | getRequest()->query('id', 0); 38 | $createDbName = $connection->getDb(); 39 | 40 | if ($selectIndex == 0) { 41 | $selectIndex = ''; 42 | } 43 | // 数据库名 + id 类似这样的 order_database_1, 好处是会根据上下文自动切库 44 | $dbName = sprintf('%s%s', $createDbName, (string)$selectIndex); 45 | $connection->db($dbName); 46 | } 47 | } 48 | ``` 49 | 50 | ## DbSelector 配置 51 | 52 |

使用 DbSelector 必须要实现 DbSelectorInterface 接口

53 | 54 | 55 | >实现了 DbSelector 还没完 还需在`bean.php`文件配置中定义 `dbSelector`属性 56 | 57 | 例如: 58 | ```php 59 | 'db2' => [ 60 | 'class' => Database::class, 61 | 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 62 | 'username' => 'root', 63 | 'password' => 'swoft123456', 64 | 'dbSelector' => bean(DbSelector::class) 65 | ], 66 | 'db2.pool' => [ 67 | 'class' => Pool::class, 68 | 'database' => bean('db2') 69 | ], 70 | 71 | ``` 72 | 73 | - dbSelector 是上面例子实现的类, 这样每次操作都会调用它 自动切库, 让分库变得简单 74 | 75 | 使用这种好处是, 你可以像没有分库一样使用, 大大降低了维护成本. 官方推荐使用这种方案 76 | 77 | 如果不满足这种的话你只能在操作的时候使用 `db()` 方法手动指定数据库了, 这样容错成本会大大降低 78 | 79 | ```php 80 | // 实体使用方式 81 | User::db('test2')->insertGetId([ 82 | 'name' => uniqid(), 83 | 'password' => md5(uniqid()), 84 | 'age' => mt_rand(1, 100), 85 | 'user_desc' => 'u desc', 86 | 'foo' => 'bar', 87 | 'xxxx' => '223asdf' 88 | ]); 89 | // DB 使用方式 90 | DB::table('user')->db('test2')->insertGetId([ 91 | 'name' => uniqid(), 92 | 'password' => md5(uniqid()), 93 | 'age' => mt_rand(1, 100), 94 | 'user_desc' => 'u desc', 95 | ]); 96 | 97 | ``` 98 | -------------------------------------------------------------------------------- /zh-CN/websocket-server/message-send.md: -------------------------------------------------------------------------------- 1 | # 消息发送 2 | 3 | 上一节我们知道了如何创建ws模块,并通过客户端连接到server。 4 | 5 | 可以从示例代码里看到有简单的消息发送使用了。 6 | 7 | ```php 8 | ... 9 | /** @var \Swoole\WebSocket\Server $server */ 10 | $server->push($fd, 'hello, welcome! :)'); 11 | ... 12 | ``` 13 | 14 | - 这里的server是swoole的 `\Swoole\WebSocket\Server` 对象 15 | - `$fd` 是与客户端的连接 ID,它表明了不同的客户端 16 | 17 | ### 使用 `\server()` 18 | 19 | 除了使用 `$server` 来发送消息外,我们还可以使用swoft封装好的 `\server()` 或者 `\Swoft::server()` 来发送消息. 20 | 21 | 例如: 22 | 23 | ```php 24 | \server()->sendTo($fd, 'hi, 你好啊!'); 25 | \Swoft::server()->sendTo($fd, 'hi, 你好啊!'); 26 | ``` 27 | 28 | 说明: 29 | 30 | - 是 `Swoft\WebSocket\Server\WebSocketServer` 的实例对象 31 | - 内部已经封装了各种发送消息的方法API 32 | - 前台运行时,通过它发送消息能从控制台看到消息发送log 33 | 34 | ## 消息发送API 35 | 36 | 注意下面的方法都在类: `Swoft\WebSocket\Server\WebSocketServer` 37 | 38 | ### 发送给某个客户端 39 | 40 | ```php 41 | public function sendTo(int $receiver, string $data, int $sender = 0): int 42 | ``` 43 | 44 | 参数说明: 45 | 46 | - `$receiver` `int` 接收者的fd 47 | - `$data` `string` 要发送的消息数据 48 | - `$sender` `int` 发送者的fd。 _可选的_ 49 | 50 | 示例: 51 | 52 | ```php 53 | \server()->sendTo($fd, 'hi, 你好啊!'); 54 | ``` 55 | 56 | ### 发送给指定的一些客户端 57 | 58 | ```php 59 | public function sendToSome(string $data, array $receivers = [], array $excluded = [], int $sender = 0, int $pageSize = 50): int 60 | ``` 61 | 62 | 参数说明: 63 | 64 | - `$data` `string` 要发送的消息数据 65 | - `$receivers` `int[]` 指定的接收者fd 列表 66 | - `$excluded` `int[]` 排除的接收者fd 列表 67 | - `$sender` `int` 发送者的fd。 _可选的_ 68 | 69 | 方法说明: 70 | 71 | - 当 `$receivers` 有数据时,将会忽略 `$excluded`。 此时就是将消息指定的发给这些接收者 72 | - 当 `$receivers` 为空时 73 | - 若 `$excluded` 有值,将会给除了这些人之外的发送消息 74 | - 若 `$excluded` 为空,相当于给所有人发消息 75 | 76 | 示例: 77 | 78 | ```php 79 | \server()->sendToSome('hi, 你们好啊!', [$fd0, $fd1, ...]); 80 | ``` 81 | 82 | ### 广播消息 83 | 84 | 发送消息给除了 `sender` 外的所有人。使用分页方式发送,每 50 个一页,直到全部发送完毕 85 | 86 | ```php 87 | broadcast(string $data, array $receivers = [], array $excluded = [], int $sender = 0): int 88 | ``` 89 | 90 | ### 发送给所有客户端 91 | 92 | ```php 93 | public function sendToAll(string $data, int $sender = 0, int $pageSize = 50): int 94 | ``` 95 | 96 | 发送消息给所有客户端,相当于进行全员广播。使用分页方式发送,每 50 个一页,直到全部发送完毕 97 | 98 | 参数说明: 99 | 100 | - `$data` `string` 要发送的消息数据 101 | - `$sender` `int` 发送者的fd。 _可选的_ 102 | 103 | 示例: 104 | 105 | ```php 106 | \server()->sendToAll('hi, 大家好啊!'); 107 | ``` 108 | 109 | ### send 110 | 111 | **参数跟 `sendToSome` 一样** 112 | 113 | 会自动根据参数判断调用上面的(`sendTo`, `sendToAll`, `sendToSome`)中的一个方法 114 | 115 | -------------------------------------------------------------------------------- /zh-CN/quick-start/start-swoft.md: -------------------------------------------------------------------------------- 1 | # 服务启动与管理 2 | 3 | Swoft 拥有完善的命令行工具,和相应的服务器管理命令 4 | 5 | ## 帮助命令 6 | 7 | ```text 8 | [root@swoft]# php bin/swoft -h 9 | Console application description (Version: 2.0.0) 10 | 11 | Usage: 12 | bin/swoft COMMAND [arg0 arg1 arg2 ...] [--opt -v -h ...] 13 | 14 | Options: 15 | --debug Setting the application runtime debug level(0 - 4) 16 | --no-color Disable color/ANSI for message output 17 | -h, --help Display this help message 18 | -V, --version Show application version information 19 | --expand Expand sub-commands for all command groups 20 | 21 | Available Commands: 22 | http Provide some commands to manage the swoft HTTP Server(alias: httpserver,httpServer,http-server) 23 | rpc Class ServiceServerCommand 24 | test Class TestCommand 25 | ws Provide some commands to operate swoft WebSocket Server(alias: ws-server,wsserver,websocket) 26 | 27 | More command information, please use: bin/swoft COMMAND -h 28 | ``` 29 | 30 | > `reload`只是重新释放一下内存,只在开发过程中有用,线上请勿使用。 31 | 32 | > 更新代码时可以放心使用`restart`,`swoole`底层做了优化,不会中断正在处理的请求 33 | 34 | ## HTTP 服务器 35 | 36 | ```bash 37 | // 启动服务 38 | php bin/swoft http:start 39 | 40 | // 守护进程启动 41 | php bin/swoft http:start -d 42 | 43 | // 重启 44 | php bin/swoft http:restart 45 | 46 | // 重新加载 47 | php bin/swoft http:reload 48 | 49 | // 关闭服务 50 | php bin/swoft http:stop 51 | ``` 52 | 53 | ## WebSocket服务器 54 | 55 | 启动WebSocket服务器,可选是否 **同时支持http处理** 56 | 57 | ```bash 58 | // 启动服务 59 | php bin/swoft ws:start 60 | 61 | // 守护进程启动 62 | php bin/swoft ws:start -d 63 | 64 | // 重启 65 | php bin/swoft ws:restart 66 | 67 | // 重新加载 68 | php bin/swoft ws:reload 69 | 70 | // 关闭服务 71 | php bin/swoft ws:stop 72 | ``` 73 | 74 | ## RPC 服务器 75 | 76 | 使用独立的RPC服务器 77 | 78 | ```bash 79 | // 启动服务 80 | php bin/swoft rpc:start 81 | 82 | // 守护进程启动 83 | php bin/swoft rpc:start -d 84 | 85 | // 重启 86 | php bin/swoft rpc:restart 87 | 88 | // 重新加载 89 | php bin/swoft rpc:reload 90 | 91 | // 关闭服务 92 | php bin/swoft rpc:stop 93 | ``` 94 | 95 | ## 启动信息 96 | 97 | 默认情况下,启动时会打印一些启动信息到控制台。 98 | 99 | > 如果你的 `.env` 开启了 `SWOFT_DEBUG=1` 将会看到更多详细的启动与加载信息。 100 | 101 | 如果你想关闭这些信息,可以在 `app/Application` 添加: 102 | 103 | ```php 104 | 105 | public function getCLoggerConfig(): array 106 | { 107 | $config = parent::getCLoggerConfig(); 108 | // disable print console start log 109 | $config['enable'] = false; 110 | 111 | return $config; 112 | } 113 | ``` 114 | 115 | 重启swoft,可以看到不会有任何信息输出了 116 | 117 | -------------------------------------------------------------------------------- /zh-CN/rpc-server/index.md: -------------------------------------------------------------------------------- 1 | # RPC 服务 2 | 3 | # RPC 服务 4 | 5 | RPC,是一种远程调用方式(Remote Procedure Call),通过RPC我们可以像调用本地方法一样调用别的机器上的方法,用户将无感服务器与服务器之间的通讯。RPC在微服务当中起到相当大的作用,当然RPC不是微服务必须的一种方式,有别的方式也可以实现这种远程调用例如RESTful API就可以实现远程调用。如果有用过SOAP那么你使用RPC将会觉得很类似,都是可以直接调用别的机器上的方法。 6 | 7 | 随着业务的发展我们的项目从简单的单体结构逐渐的演化成微服务结构,我们为什么要拆分成微服务呢?那我们来说说微服务和单体架构的优缺点。我们看一下单体架构图。 8 | 9 | ## 单体架构 10 | 11 | ![单体架构](../image/rpc-server/1.png) 12 | 13 |
单体架构图
14 | 15 | ### 单体架构优点 16 | 17 | * 部署容易,如php写的项目,只要一个文件夹复制到支持php的环境就可以了,java只需要一个jar包 18 | * 测试容易,我们整体项目只要改了一个地方马上就可以测试得出结果 19 | * 负载均衡就可以解决,快速部署多个一模一样的项目在不同的机器运行分流 20 | 21 | ### 单体架构的缺点 22 | 23 | * 部署的问题,对于php来说这点还好,但是对于java的项目来说,我们需要重新打包整个项目耗费的时间是很长的 24 | * 代码维护,由于所有的代码都写在一个项目里面,要想要修改某一个功能点那么需要对项目的整体逻辑和设计有较深的理解,否则代码耦合严重,导致维护难,特别对于新入职的员工来说这将是最容易出现问题的地方 25 | * 开发效率低,随着项目需求的不断改变和新的功能新增,老旧的代码又不敢随便删除,导致整个项目变得笨重,这将会增加你阅读代码的时间 26 | * 扩展性,在高并发的情况下,我们往往不是整个项目的每一个功能都处于高流量高请求的情况下的,很多时候都是某一个功能模块使用的人数比较多,在单体结构下我们没有办法针对单个功能实现分布式扩展,必须整个项目一起部署 27 | 28 | ## 微服务架构 29 | 30 | 在2014年被提出,现在国内很多公司已经使用,微服务是一种架构设计,并不是说什么框架或者代替什么。微服务做的事情是按照项目颗粒度进行服务的拆分,把模块单独拿出来做成每一个单独的小项目。微服务的主要特点有:每一个功能模块是一个小项目、独立运行在不同进程或者机器上、不同功能可以又不同的人员开发独立开发不松耦合、独立部署不需要依赖整体项目就可以启动单个服务、分布式管理。每一个服务只要做好自己的事情就好了。在设计微服务的时候还需要考虑到数据库的问题,是所有微服务使用共同一个数据库还是每一个服务单个数据库 31 | 32 | ### 微服务优点 33 | 34 | * 拆分业务,把整体大项目分割成不同小项目运行在不同进程或者机器上实现数据隔离 35 | * 技术栈,每个服务可以由不同的团队或者开发者进行开发,外部调用人员不需要操心具体怎么实现的,只需要类似调用自己方法一样或者接口一样按照服务提供者给出来的参数传递即可 36 | * 独立部署,每一个服务独立部署,部署一个服务不会影响整体项目,如果部署失败最多是这个服务的功能缺失,并不影响其他功能的使用 37 | * 按需部署,针对不同的需求可以给不同的服务自由扩展服务器,根据服务的规模部署满足需求的实例 38 | * 局部修改,当一个服务有新需求或者其他修改,不需要修改整体项目只要管好自己的服务就好了 39 | 40 | ### 微服务缺点 41 | 42 | * 运维,微服务由于把业务拆分得细,有可能部署在不同机器上,因此对于运维人员的管理来说,这部分的成本会加大 43 | * 接口调整,微服务之间通过接口进行通信。如果修改某个微服务的API,可能所有使用了该接口的微服务都需要做调整; 44 | * 重复劳动,很多服务可能都会使用到相同的功能。而这个功能并没有达到分解为一个微服务的程度,这个时候,可能各个服务都会开发这一功能,导致代码重复。 45 | * 分布式,由于会把不同服务部署在不同机器上,那么对于这些服务的调用、容错、网络延迟、分布式事务等等都是一个很大的挑战,当然微服务不一定全部都是部署在不同服务器上 46 | 47 | ## 服务调用 48 | 49 | ![微服务调用](../image/rpc-server/2.png) 50 | 51 |
微服务调用
52 | 53 | 如上图所示,RPC就用于调用者与服务之间的通讯,RPC协议可基于TCP、UDP或者HTTP实现,但是更推荐选择TCP。 54 | 55 | 例如调用者需要调用商品的服务就可以通过RPC或者RESTful API来调用,那么RPC调用和RESTful API两者之间的区别在哪呢? 56 | 57 | - TCP 支持长连接,当调用服务的时候不需要每次都进行三次握手才实现。从性能和网络消耗来说RPC都具备了很好的优势。 58 | - RESTful API 基于HTTP的,也就是说每次调用服务都需要进行三次握手建立起通信才可以实现调用,当我们的并发量高的时候这就会浪费很多带宽资源 59 | - 服务对外的话采用RESTful API会比RPC更具备优势,因此看自己团队的服务是对内还是对外 60 | 61 | ### RPC调用过程 62 | 63 | ![RPC的调用过程](../image/rpc-server/3.png) 64 | 65 |
RPC调用过程
66 | 67 | > RPC最主要的作用就是用于服务调用 68 | 69 | 本文作为RPC的使用场景开山篇,对于单体架构和微服务的进行了一个描述。这个就是RPC的一个使用场景,也是最常用的一个使用场景。大家只有了解好RPC是什么使用在什么场景才能更好的去使用。 70 | 71 | Swoft给我们提供了RPC的底层服务,我们并不需要去关心底层通讯细节和调用的过程。 72 | 73 | Swoft通过定义接口,实现接口,启动RPC Server 提供接口服务。我们只需要简单的写好几个类就可以实现一个简单RPC模块。 74 | -------------------------------------------------------------------------------- /zh-CN/http-server/setting.md: -------------------------------------------------------------------------------- 1 | # Http Server 配置参数 2 | 3 | 在应用下的 `app/bean.php` 配置server,下面列举了一些简单的配置,你也可以自由组合同时提供多种服务。 4 | 5 | ## 可配置项 6 | 7 | 可配置项用于 http server bean 配置,除了 `class` 其他都是 http server 的属性。 8 | 9 | - `class` 指定 `Http Server` 的处理类 10 | - `port` 指定 Http Server 的端口 11 | - `listener` 指定其他一同启动的服务,添加端口服务监听,可以多个。 12 | - rpc 启动 `RPC` 服务 13 | - `on` 配置监听的事件 14 | - 注册事件、设置对应事件的处理监听,事件触发组件调用,在任务里面使用 15 | - `setting` 这里是参考 [Swoole Server配置选项](https://wiki.swoole.com/wiki/page/274.html) 16 | - `pidFile` 设置进程 `pid文件` 位置,默认值 `@runtime/swoft.pid` 17 | - `mode` 运行的模式,参考 [Swoole Server 构造函数](https://wiki.swoole.com/wiki/page/14.html) 第三个参数 18 | - `type` 指定Socket的类型,支持TCP、UDP、TCP6、UDP6、UnixSocket Stream/Dgram 等 [Swoole Server 构造函数](https://wiki.swoole.com/wiki/page/14.html) 第四个参数 19 | 20 | ## 基础配置 21 | 22 | Swoft应用的 Http Server 配置在 `app/bean.php` 中。在这个文件里,你可以看到 Http Server数组里面包含了 Http Server 的基本信息。 23 | 24 | ```php 25 | 'httpServer' => [ 26 | 'class' => HttpServer::class, 27 | 'port' => 18306, 28 | /* @see HttpServer::$setting */ 29 | 'setting' => [ 30 | 'log_file' => alias('@runtime/swoole.log'), 31 | ] 32 | ], 33 | ``` 34 | 35 | ## 启用Task处理 36 | 37 | 启用Task进程处理任务 38 | 39 | ```php 40 | 'httpServer' => [ 41 | 'class' => HttpServer::class, 42 | 'port' => 18306, 43 | 'on' => [ 44 | // Enable task must task and finish event 45 | SwooleEvent::TASK => \bean(TaskListener::class), 46 | SwooleEvent::FINISH => \bean(FinishListener::class) 47 | ], 48 | /* @see HttpServer::$setting */ 49 | 'setting' => [ 50 | 'task_worker_num' => 12, 51 | 'task_enable_coroutine' => true 52 | ] 53 | ], 54 | ``` 55 | 56 | ## 启用RPC支持 57 | 58 | ```php 59 | 'httpServer' => [ 60 | 'class' => HttpServer::class, 61 | 'port' => 18306, 62 | 'listener' => [ 63 | 'rpc' => \bean('rpcServer') 64 | ], 65 | /* @see HttpServer::$setting */ 66 | 'setting' => [ 67 | 'task_worker_num' => 12, 68 | 'task_enable_coroutine' => true 69 | ] 70 | ], 71 | 'rpcServer' => [ 72 | 'class' => ServiceServer::class, 73 | 'port' => 18308, 74 | ], 75 | ``` 76 | 77 | ## 启用https支持 78 | 79 | 在swoft里启用 https 支持也非常简单,添加如下的配置即可: 80 | 81 | ```php 82 | 'httpServer' => [ 83 | 'type' => SWOOLE_SOCK_TCP | SWOOLE_SSL, 84 | 85 | /* @see HttpServer::$setting */ 86 | 'setting' => [ 87 | 'ssl_cert_file' => '/my/certs/2288803_www.domain.com.pem', 88 | 'ssl_key_file' => '/my/certs/2288803_www.domain.com.key', 89 | ] 90 | ] 91 | ``` 92 | 93 | > 注意: 你必须安装 OpenSSL 库,并且确保安装swoole时是启用了 ssl 选项的。同时,需要设置 `'type' => SWOOLE_SOCK_TCP | SWOOLE_SSL` 94 | -------------------------------------------------------------------------------- /zh-CN/quick-start/project-skeleton.md: -------------------------------------------------------------------------------- 1 | # 应用结构 2 | 3 | 一个完整的swoft应用可以包含: 4 | 5 | - console 应用 6 | - http 服务(跟传统的框架差不多) 7 | - websocket 服务 8 | - rpc 服务 9 | - tcp 服务 10 | 11 | > `swoft-cloud/swoft` 即是一个完整应用的demo。当然,如果你只想使用一部分功能也是可以的 12 | 13 | ## 应用骨架 14 | 15 | > `app` 下的类目录为了避免一些文件夹名称没有复数单词,导致命名不统一,所有的文件夹名称 **统一使用单数** 16 | 17 | ```text 18 | ├── app/ ------ 应用代码目录 19 | │ ├── Annotation/ ------- 定义注解相关 20 | │ ├── Aspect/ ------- AOP 切面 21 | │ ├── Common/ ------- 一些具有独立功能的 class bean 22 | │ ├── Console/ ------- 命令行代码目录 23 | │   │   ├── Command/ 24 | │ ├── Exception/ ------ 定义异常类目录 25 | │ │ └── Handler/ ------ 定义异常处理类目录 26 | │   ├── Http/ ------ HTTP 服务代码目录 27 | │   │   ├── Controller/ 28 | │   │   └── Middleware/ 29 | │   ├── Helper/ 30 | │   │   └── Functions.php 31 | │ ├── Listener/ ------ 事件监听器目录 32 | │ ├── Model/ ------ 模型、逻辑等代码目录(这些层并不限定,根据需要使用) 33 | │   │   ├── Dao/ 34 | │   │   ├── Data/ 35 | │   │   ├── Logic/ 36 | │ │ └── Entity/ 37 | │ ├── Rpc/ ------ RPC 服务代码目录 38 | │ │ └── Service/ 39 | │   │   └── Middleware/ 40 | │   ├── WebSocket/ ------ WebSocket 服务代码目录 41 | │   │   ├── Chat/ 42 | │   │   ├── Middleware/ 43 | │   │   └── ChatModule.php 44 | │ ├── Tcp/ ------ Tcp 服务代码目录 45 | │ │ └── Controller/ ------ Tcp 服务处理控制器目录 46 | │ ├── Application.php -------- 应用类文件继承自swoft核心 47 | │ ├── AutoLoader.php -------- 项目扫描等信息(应用本身也算是一个组件) 48 | │ └── bean.php 49 | ├── bin/ 50 | │   ├── bootstrap.php 51 | │   └── swoft ------ swoft 入口文件 52 | ├── config/ ------ 应用配置目录 53 | │ ├── base.php --- 基础配置 54 | │ └── db.php 55 | ├── public/ ------ WEB可访问目录 56 | ├── resource/ ------ 应用相关资源目录 57 | │   ├── language/ ------ 语言资源目录 58 | │   └── view/ ------ 视图资源目录 59 | ├── runtime/ ------ 临时文件目录(日志、上传文件、文件缓存等) 60 | ├── test/ ------ 单元测试代码目录 61 | │   └── bootstrap.php 62 | ├── composer.json 63 | ├── composer.lock 64 | ├── phar.build.inc 65 | └── phpunit.xml.dist 66 | ``` 67 | 68 | > render by `tree -L 2 -F --dirsfirst` 69 | 70 | 71 | ## 组件骨架结构 72 | 73 | ``` 74 | ├── src/ 75 | │ ├── Annotation/ -------- 组件注解类定义 76 | │ ├── Common/ ------- 一些具有独立功能的 class bean 77 | │ ├── Concern/ ------- abstract, traits classes 78 | │ ├── Contract/ ------- interface classes 79 | │ ├── Exception/ 80 | │ ├── Helper/ 81 | │ ├── Listener/ 82 | │ ├── AutoLoader.php -------- 组件扫描等信息 83 | ├── test/ ------ 单元测试代码目录 84 | │ ├── unit/ 85 | │ ├── testing/ 86 | │ └── bootstrap.php 87 | ├── LICENSE 88 | ├── README.md 89 | ├── composer.json 90 | └── phpunit.xml 91 | ``` 92 | 93 | > `src/AutoLoader.php` 是一个组件必须存在的文件,swoft依据它来确定要扫描那些目录 94 | 95 | ### 开发自定义组件 96 | 97 | 在2.0版本你可以非常容易的开发一个swoft组件。延伸阅读 [开发自定义组件](../component/index.md) 98 | -------------------------------------------------------------------------------- /zh-CN/task/setting.md: -------------------------------------------------------------------------------- 1 | # 配置与启用 2 | 3 | 任务配置参数,可以直接在对应的 `Server->setting` 配置即可,如果要启用任务更简单,`Server` 新增一个 `on` 事件。 4 | 5 | ## 协程任务 6 | 7 | Http Server 配置开启任务为例: 8 | 9 | ```php 10 | return [ 11 | 'httpServer' => [ 12 | // ... 13 | 'on' => [ 14 | SwooleEvent::TASK => \bean(TaskListener::class), // Enable task must task and finish event 15 | SwooleEvent::FINISH => \bean(FinishListener::class) 16 | ], 17 | /* @see HttpServer::$setting */ 18 | 'setting' => [ 19 | 'task_worker_num' => 12, 20 | 'task_enable_coroutine' => true 21 | ] 22 | ], 23 | ] 24 | ``` 25 | 26 | - `task_enable_coroutine` 必须为 `true` 27 | - task 事件和finish 事件必须配置,且为 `TaskListener::class` 和 `FinishListener::class` 28 | 29 | Rpc Server 配置开启任务为例: 30 | 31 | ```php 32 | return [ 33 | 'rpcServer' => [ 34 | // ... 35 | 'on' => [ 36 | SwooleEvent::TASK => \bean(TaskListener::class), // Enable task must task and finish event 37 | SwooleEvent::FINISH => \bean(FinishListener::class) 38 | ], 39 | /* @see HttpServer::$setting */ 40 | 'setting' => [ 41 | 'task_worker_num' => 12, 42 | 'task_enable_coroutine' => true 43 | ] 44 | ], 45 | ] 46 | ``` 47 | 48 | wsServer Server 配置开启任务为例: 49 | 50 | ```php 51 | return [ 52 | 'wsServer' => [ 53 | // ... 54 | 'on' => [ 55 | SwooleEvent::TASK => \bean(TaskListener::class), // Enable task must task and finish event 56 | SwooleEvent::FINISH => \bean(FinishListener::class) 57 | ], 58 | /* @see HttpServer::$setting */ 59 | 'setting' => [ 60 | 'task_worker_num' => 12, 61 | 'task_enable_coroutine' => true 62 | ] 63 | ], 64 | ] 65 | ``` 66 | 67 | > 任务配置与启用,在 `Http Server` / `Rpc Server` / `Websocket Server` 都完全一样,启用任务需要监听 `task` `finish` 两个事件。 68 | 69 | ## 同步阻塞任务 70 | 71 | Swoft 不仅提供协程任务,并且支持同步任务,同步任务和协程任务只能选择一种运行,两种不能同时存在。同步任务只需配置 `task` 事件,不支持异步 `finish` 事件。官方建议使用协程任务实现业务, 72 | 如果需要通过任务实现MongoDB、PostgreSQL 类似这种场景才使用同步任务。 73 | 74 | > 2.0.4+ 支持 75 | 76 | 如下已 Http-server 为例: 77 | 78 | ```php 79 | return [ 80 | 'httpServer' => [ 81 | // ... 82 | 'on' => [ 83 | SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task 84 | ], 85 | /* @see HttpServer::$setting */ 86 | 'setting' => [ 87 | 'task_worker_num' => 6, 88 | 'task_enable_coroutine' => false 89 | ] 90 | ], 91 | ] 92 | ``` 93 | 94 | - `task_enable_coroutine` 必须设置为 `false` 95 | - task 事件必须是 `SyncTaskListener::class` 96 | 97 |

同步阻塞任务,不能直接使用框架提供的所有 IO 操作(数据库、缓存、RPC等等)以及应用日志,控制器日志可以使用。 同步阻塞任务的定义和使用与协程任务一样,但是没有上下文。

-------------------------------------------------------------------------------- /zh-CN/validator/controller-validator.md: -------------------------------------------------------------------------------- 1 | # 控制器中使用 2 | 3 | 如果想在控制器中使用验证器很简单,只需要是一个注解 `@Validate` 就行 4 | 5 | - 一个 `action` 可以定义多个 `@Validate` 使用多个验证器 6 | - 多个验证器按照配置顺序验证 7 | 8 | 如下定义一个 `ValidatorController`, 同时使用默认验证器和自定义验证器以及我们自定义的验证规则。 9 | 10 | ```php 11 | getParsedBody(); 39 | } 40 | 41 | /** 42 | * 仅验证TestValidator验证器中的 type 字段 43 | * 44 | * @RequestMapping() 45 | * @Validate(validator="TestValidator",fields={"type"}) 46 | * @param Request $request 47 | * 48 | * @return array 49 | */ 50 | public function validateType(Request $request): array 51 | { 52 | return $request->getParsedBody(); 53 | } 54 | 55 | /** 56 | * 仅验证TestValidator验证器中的 password 字段 password字段使用的是自定义的验证规则。 57 | * 58 | * @RequestMapping() 59 | * @Validate(validator="TestValidator",fields={"password"}) 60 | * @param Request $request 61 | * 62 | * @return array 63 | */ 64 | public function validatePassword(Request $request): array 65 | { 66 | return $request->getParsedBody(); 67 | } 68 | 69 | /** 70 | * 使用userValidator自定义验证器 71 | * 72 | * @RequestMapping() 73 | * @Validate(validator="userValidator") 74 | * @param Request $request 75 | * 76 | * @return array 77 | */ 78 | public function validateCustomer(Request $request): array 79 | { 80 | return $request->getParsedBody(); 81 | 82 | } 83 | } 84 | 85 | ``` 86 | 87 | - `$request->getParsedBody()` 所有解析数据 88 | - `$request->parsedBody('key', 'default')` 指定 KEY 解析数据 89 | - `$request->getParsedQuery()` 所有解析的 Query 参数 90 | - `$request->parsedQuery('key', 'default')` 指定 KEY 解析数据(>=2.0.2) 91 | 92 | ## @Validate 93 | 94 | - validator 指定验证器名称 95 | - fields 指定验证器里面验证的字段,这样可以高效的重复使用验证器 96 | - type 默认 `body`,`ValidateType::GET` 验证 GET 请求 query 参数 97 | - params 自定义验证器使用,传递给自定义验证器的参数 98 | 99 | > 注意 `$request->getParsedBody()` 获取的请求数据,是已经通过验证器修改的数据。验证器可以支持表单、请求 body 数据验证,但是 body 验证需要定义对应的数据解析器,框架默认提供 JSON/XML 类型数据解析器,详细介绍,请参考 Http Server 章节。 100 | -------------------------------------------------------------------------------- /zh-CN/http-server/controller.md: -------------------------------------------------------------------------------- 1 | # 控制器 Controller 2 | 3 | 控制器作为HTTP服务的核心组件,串接起一次请求的整个生命周期. 通过 注解 的方式,相较于传统的 Controller,代码更简洁,用户可以更关注业务逻辑。 4 | 5 | ## 创建控制器 6 | 7 | 主要通过 `@Controller` 注解实现。代码可以放置任意位置,不过为了统一标准,建议放在 `app/Http/Controller` 下 8 | 9 | ## 注解 10 | 11 | ### Controller 12 | 13 | Http 控制器类注解tag `@Controller` 14 | 15 | - 注解类: `Swoft\Http\Server\Annotation\Mapping\Controller` 16 | - 作用范围: `CLASS` 17 | - 拥有属性: 18 | - `prefix` 指定路由前缀 19 | 20 | > 通常仅有 `@Controller` 是没有什么效果的,它需要配合接下来的 `@RequestMapping` 一起才能正确的工作。 21 | 22 | ## 使用 23 | 24 | - 显式指定路由前缀: `@Controller(prefix="/index")` 或 `@Controller("/index")` 25 | - 隐式指定路由前缀: `@Controller()` 默认自动解析 controller class 的名称,并且使用小驼峰格式。 26 | 27 | ```php 28 | @Controller 和 @RequestMapping 的搭配使用。关于 `@RequestMapping` 注解详细参考:[路由](route.md) 104 | 105 | ## 注意 106 | 107 | 在Swoft里不要按照传统的fpm框架继承父类控制器的成员属性在其他控制器使用,这种做法是错误的。 108 | 109 | 如: 110 | 111 | ```php 112 | /** 113 | * @Controller() 114 | */ 115 | class BaseController 116 | { 117 | protected $num; 118 | } 119 | 120 | /** 121 | * @Controller(prefix="/v1/index") 122 | */ 123 | class IndexController extends BaseController 124 | { 125 | /** 126 | * @RequestMapping(route="index") 127 | */ 128 | public function index() 129 | { 130 | $this->num++; 131 | echo $this->num."\n"; 132 | } 133 | } 134 | ``` 135 | 136 | -------------------------------------------------------------------------------- /zh-CN/config/config.md: -------------------------------------------------------------------------------- 1 | # 应用配置 2 | 3 | ## bean配置 4 | 5 | 应用配置数据是由一个bean对象管理的,可以在 `app/bean.php` 文件设置应用配置参数 6 | 7 | ```php 8 | return [ 9 | 'config' => [ 10 | 'path' => __DIR__ . '/../config', 11 | ], 12 | ]; 13 | ``` 14 | 15 | 可配置项: 16 | 17 | - `path` 自定配置文件路径 18 | - `base` 主文件名称,默认 `base` (_其他文件的数据都会按文件名为key合并到主文件数据中_) 19 | - `type` 配置文件类型,默认 `php` 同时也支持 `yaml` 格式 20 | - `parser` 配置解析器,默认已经配置 php/yaml 解析器。 21 | - `env` 配置当前环境比如 `dev/test/pre/pro` 22 | 23 | ## 数据格式 24 | 25 | 配置目录所有配置文件会解析成一个数组,但是不会递归合并数据,只会合并当前目录文件数据,以它的文件名称为数组 key 进行合并数组。 26 | 比如 `config` 目录配置文件如下: 27 | 28 | ``` 29 | |-- base.php 30 | |-- data.php 31 | `-- pro 32 | |-- base.php 33 | `-- data.php 34 | ``` 35 | 36 | > 只会解析当前目录文件数据,不会递归解析数据。当前使用 env 配置时,环境目录里面的配置信息会覆盖最外层文件名称相同的数据。提醒:配置文件里面可以使用 `env()`函数读取环境配置。 37 | 38 | - `config/base.php` 39 | 40 | ```php 41 | return [ 42 | 'key' => 'value' 43 | ]; 44 | ``` 45 | 46 | - `config/data.php` 47 | 48 | ```php 49 | return [ 50 | 'dkey' => [ 51 | 'dvalue' 52 | ], 53 | 'key' => 'value' 54 | ]; 55 | ``` 56 | 57 | - `config/pro/base.php` 58 | 59 | ```php 60 | return [ 61 | 'key' => 'valuePro' 62 | ]; 63 | ``` 64 | 65 | - `config/pro/data.php` 66 | 67 | ```php 68 | return [ 69 | 'dkey' => [ 70 | 'dvalue' 71 | ], 72 | 'key' => 'valuePro' 73 | ]; 74 | ``` 75 | 76 | 如上配置文件,当不配置 config 的 `env` 参数,合并的数据格式如下: 77 | 78 | ```php 79 | return [ 80 | 'key' => 'value', 81 | 'data' => [ 82 | 'dkey' => [ 83 | 'dvalue' 84 | ], 85 | 'key' => 'value' 86 | ] 87 | ]; 88 | ``` 89 | 90 | 当配置 config 对象的 `'env' => 'pro'` 参数,合并的数据格式如下: 91 | 92 | ```php 93 | return [ 94 | 'key' => 'valuePro', 95 | 'data' => [ 96 | 'dkey' => [ 97 | 'dvalue' 98 | ], 99 | 'key' => 'valuePro' 100 | ] 101 | ]; 102 | ``` 103 | 104 | 105 | ## 获取配置 106 | 107 | 框架提全局函数、注解、config 对象多种方式,使用应用配置数据。 108 | 109 | ### 函数 110 | 111 | 全局函数使用 `config()` 112 | ``` 113 | config(string $key = null, mixed $default = null):mixed 114 | ``` 115 | 116 | - key 配置参数 key,子数组可以使用 `.` 分割,比如上面的例子 `data.dkey` 可以获取到 `["dvalue"]`, 当`key=null` 获取所有配置参数 117 | - default 默认参数,如果 key 参数不存在,返回默认值,默认值可以是任意类型 118 | 119 | ### 注解 120 | 121 | 通过容器使用注解的方式,注入配置到属性值。 122 | 123 | ```php 124 | use Swoft\Bean\Annotation\Mapping\Bean; 125 | use Swoft\Config\Annotation\Mapping\Config; 126 | 127 | /** 128 | * @Bean() 129 | */ 130 | class Demo 131 | { 132 | /** 133 | * @Config("data.dkey") 134 | */ 135 | private $dvalue = []; 136 | 137 | // ... 138 | } 139 | ``` 140 | 141 | 此例子和上面功能一样,都是读取相同的数据,两种不同的方式。 142 | 143 | > 使用注解,一定要保证类是一个bean对象(通过其它注解注入到容器) 144 | 145 | ### 对象 146 | 147 | 如果上面两种方式还不能满足你的业务需求,你可以从容器里面获取配置对象,里面自带很多方式操作配置数据。 148 | 149 | ```php 150 | $config = \Swoft::getBean('config'); 151 | ``` 152 | 153 | config 对象常用方法 154 | 155 | - get(string $key, $default = null) 获取参数 156 | - offsetGet($key) 获取参数 157 | - .... 158 | -------------------------------------------------------------------------------- /zh-CN/aop/usage.md: -------------------------------------------------------------------------------- 1 | # 使用 2 | 3 | 本章以计算类里面每个方法执行时间为例,简单介绍使用切面编程。 4 | 5 | ## 编写测试类 6 | 7 | 首先先编写一个Http Controller 类 8 | 9 | > App\Http\Controller\TestExecTimeController 10 | 11 | ```php 12 | App\Aspect\CalcExecTimeAspect 72 | 73 | ```php 74 | start = microtime(true); 106 | } 107 | 108 | /** 109 | * 定义通知点 110 | * @After() 111 | */ 112 | public function after(JoinPoint $joinPoint) 113 | { 114 | $method = $joinPoint->getMethod(); 115 | $after = microtime(true); 116 | $runtime = ($after - $this->start) * 1000; 117 | 118 | echo "{$method} 方法,本次执行时间为: {$runtime}ms\n"; 119 | } 120 | } 121 | ``` 122 | 123 | 启动 swoft 之后正常的访问我们的测试控制器,将会在控制器看到输出 124 | 125 | > 访问控制器 126 | 127 | ```bash 128 | $ curl http://localhost:18306/testExecTime/test/10 129 | [3628800]% 130 | 131 | $ curl http://localhost:18306/testExecTime/sumAndSleep 132 | [500500]% 133 | ``` 134 | 135 | > 控制台输出 136 | 137 | ```bash 138 | factorial 方法,本次执行时间为: 0.10013580322266ms 139 | ``` 140 | -------------------------------------------------------------------------------- /zh-CN/tool/swoftcli/create-app-or-component.md: -------------------------------------------------------------------------------- 1 | # 创建新应用或组件 2 | 3 | 自swoftcli `v0.1.0` 起,支持通过内置命令快速的创建一个新的应用骨架,或者创建一个新的组件骨架结构。 4 | 5 | ## 查看命令组 6 | 7 | ```bash 8 | php swoftcli.phar new 9 | // or use alias 10 | php swoftcli.phar create 11 | ``` 12 | 13 | 命令组说明: 14 | 15 | - 命令组 `new` (组别名 `create`),通过任意一个都可以访问到它 16 | - 拥有两个子命令,`application` 和 `component` 分别用于创建新的应用和新的组件结构 17 | 18 | ![swoftcli-new](../../image/tool/swoftcli/swoftcli-new.png) 19 | 20 | ## 创建新应用 21 | 22 | 创建新应用是通过拉取github上已存在的模板项目仓库,因此您可以轻松自定义符合自己需求的模板。 23 | 24 | swoft默认提供了5个模板仓库,方便用户根据需要拉取不同的骨架结构。可以在下面的命令帮助中看到有哪几个默认骨架。 25 | 26 | ### 查看命令 27 | 28 | ```bash 29 | php swoftcli.phar create:app -h 30 | // or use alias 31 | php swoftcli.phar create:app 32 | ``` 33 | 34 | ![swoftcli-new-app](../../image/tool/swoftcli/swoftcli-new-app.png) 35 | 36 | ### 命令使用说明 37 | 38 | 命令参数: 39 | 40 | - `name` _string_ 设置新项目名称,同时也是作为新应用的目录名。**必须** 41 | 42 | 命令选项: 43 | 44 | - `--repo` _string_ 自定义设置模板仓库的git地址,可以是 `UERANME/REPO` 或者 完整url地址 45 | - `--type` _string_ 从默认的提供的5个模板仓库里选择一个来作为源仓库,默认是 `http` 46 | 47 | > `YOUR_APP_NAME` 指的是你的新项目名称,同时也是作为新应用的目录名。 48 | 49 | 直接使用 `create:app YOUR_APP_NAME`,默认使用 `swoft-http-project` 模板仓库。 50 | 如果需要ws/tcp/rpc等模板仓库作为基础模板,可以如下指定 `type` 选项。 51 | 52 | ```bash 53 | php swoftcli.phar create:app --type ws YOUR_APP_NAME 54 | php swoftcli.phar create:app --type tcp YOUR_APP_NAME 55 | ``` 56 | 57 | 如果你需要对模板做一些完全的自定义,那你就可以在自己的github创建需要的模板仓库,然后使用如下命令来使用: 58 | 59 | ```bash 60 | php swoftcli.phar create:app --repo UERANME/REPO YOUR_APP_NAME 61 | ``` 62 | 63 | 使用的完整的git仓库地址;这时不限于从github拉取,即也可以从自己的git服务拉取来初始化一个新应用。 64 | 65 | ```bash 66 | php swoftcli.phar create:app --repo https://github.com/UERANME/REPO.git YOUR_APP_NAME 67 | ``` 68 | 69 | ### 使用命令创建新应用 70 | 71 | 我们来试试创建新应用的命令。 72 | 73 | ```bash 74 | php swoftcli.phar create:app --type http my-swoft-app 75 | ``` 76 | 77 | 我们可以看到如下信息: 78 | 79 | ![swoftcli-new-app](../../image/tool/swoftcli/swoftcli-new-app-example.png) 80 | 81 | ## 创建新组件 82 | 83 | swoft 也提供了简单快捷的命令来创建一个新的组件骨架。 84 | 85 | > 骨架结构非常简单,包含 `composer.json` `README.md` `AutoLoader.php` 等基础文件 86 | 87 | ### 查看命令 88 | 89 | ```bash 90 | php swoftcli.phar create:component --help 91 | // or use alias 92 | php swoftcli.phar create:c 93 | ``` 94 | 95 | ![swoftcli-new-cpt](../../image/tool/swoftcli/swoftcli-new-cpt.png) 96 | 97 | ### 命令使用说明 98 | 99 | 命令参数: 100 | 101 | - `name` _string_ 设置新组件的名称,同时也是作为组件目录名。**必须** 102 | 103 | 命令选项: 104 | 105 | - `-n, --namespace` _string_ 设置组件的命名空间,设置后会自动写入到 `composer.json` 的PSR4加载。 106 | - `--no-licence` _bool_ 是否添加 LICENSE 文件,默认会添加 107 | - `-o, --output` _string_ 组件创建输出路径;不设置则创建到当前目录下 108 | - `--pgk-name` _string_ 组件的包名,会自动写入到 `composer.json`。为空则会根据当前用户名加上组件名组成 109 | 110 | ### 使用命令创建新应用 111 | 112 | 我们来试试创建一个新的组件结构。 113 | 114 | ```bash 115 | php swoftcli.phar create:component my-swoft-component -n 'MySwoft\Component' -o vendor/somedir 116 | ``` 117 | 118 | 我们可以看到如下信息: 119 | 120 | ![swoftcli-new-app](../../image/tool/swoftcli/swoftcli-new-cpt-example.png) 121 | -------------------------------------------------------------------------------- /zh-CN/tcp-server/controller.md: -------------------------------------------------------------------------------- 1 | # TCP控制器 2 | 3 | 与http server类似,tcp server中也使用对应的控制器来处理系统分发的数据请求。 4 | 5 | ## 注解 6 | 7 | tcp server新增两个注解 `@TcpControler` 和 `@TcpMapping`,由他们定义tcp控制器和处理方法。 8 | 9 | ### TcpControler 10 | 11 | 类注解 `@TcpControler` 标记当前类是一个 Tcp 控制器。 12 | 13 | - 注解类: `Swoft\Tcp\Server\Annotation\Mapping\TcpController` 14 | - 作用范围: `CLASS` 15 | - 拥有属性: 16 | + `prefix` _string_ 数据路由前缀,为空自动解析类名称为前缀。 17 | 18 | ### TcpMapping 19 | 20 | 方法注解 `@TcpMapping` 标记具体的数据处理方法,类似于http控制器里的action。 21 | 22 | - 注解类: `Swoft\Tcp\Server\Annotation\Mapping\TcpMapping` 23 | - 作用范围: `METHOD` 24 | - 拥有属性: 25 | + `route` _string_ 命令名称,为空自动使用方法名称。 26 | + `root` _bool_ 命令名称是否是顶级命令。默认 `false` 27 | 28 | ### 提示 29 | 30 | - 自动解析 `TcpControler` 的前缀时,会自动尝试去除 `Controler` 部分。eg: `DemoController` 得到 `demo` 31 | - 通常,完整的tcp命令是 上面的 `preifx` 和 `route` 由点拼接而成 `PREFIX.ROUTE`。eg: `demo.index` 32 | - 当 `TcpMapping.root` 为 `true` 时,完整命令直接是 `TcpMapping.route` 33 | 34 | ## 编写控制器 35 | 36 | ```php 37 | setData('[list]allow command: list, echo, demo.echo'); 60 | } 61 | 62 | /** 63 | * @TcpMapping("echo") 64 | * @param Request $request 65 | * @param Response $response 66 | */ 67 | public function index(Request $request, Response $response): void 68 | { 69 | $str = $request->getPackage()->getDataString(); 70 | 71 | $response->setData('[demo.echo]hi, we received your message: ' . $str); 72 | } 73 | 74 | /** 75 | * @TcpMapping("strrev", root=true) 76 | * @param Request $request 77 | * @param Response $response 78 | */ 79 | public function strRev(Request $request, Response $response): void 80 | { 81 | $str = $request->getPackage()->getDataString(); 82 | 83 | $response->setData(\strrev($str)); 84 | } 85 | 86 | /** 87 | * @TcpMapping("echo", root=true) 88 | * @param Request $request 89 | * @param Response $response 90 | */ 91 | public function echo(Request $request, Response $response): void 92 | { 93 | $str = $request->getPackage()->getDataString(); 94 | 95 | $response->setData('[echo]hi, we received your message: ' . $str); 96 | } 97 | } 98 | ``` 99 | 100 | 好了,服务端代码已经编写好了。这里我们使用默认的配置 `EOF` 分包方式,数据协议格式也使用默认的 `SimpleTokenPacker::TYPE`。 101 | 102 | 重新启动我们的 tcp server `php bin/swoft tcp:start`,接下来一篇讲述如何与我们的tcp server进行通信交互。 103 | -------------------------------------------------------------------------------- /zh-CN/extra/whoops.md: -------------------------------------------------------------------------------- 1 | # Swoft Whoops 2 | 3 | `swoft/whoops` 是对 `filp/whoops` 在swoft中使用的简单封装,用于渲染并显示更加利于阅读的异常错误信息。 4 | 5 | ![swoft-whoops](https://raw.githubusercontent.com/swoft-cloud/swoft-doc/2.x/zh-CN/image/extra/swoft-whoops.jpg) 6 | 7 | ## Github 8 | 9 | - https://github.com/swoft-cloud/swoft-whoops.git 10 | 11 | ## 安装 12 | 13 | `swoft/whoops` 作为一个额外的扩展组件,需要手动安装: 14 | 15 | - 通过 composer 命令: 16 | 17 | ```bash 18 | composer require swoft/whoops 19 | ``` 20 | 21 | - 通过 `composer.json` 配置: 22 | 23 | ```json 24 | "swoft/whoops": "~2.0.0" 25 | ``` 26 | 27 | ## 使用 28 | 29 | ### 作为中间件使用 30 | 31 | 使用 `Swoft\Whoops\WhoopsMiddleware` 作为一个全局的Http中间件(`app/bean.php`): 32 | 33 | ```php 34 | 'httpDispatcher' => [ 35 | // Add global http middleware 36 | 'middlewares' => [ 37 | // Notice: Please add to the first place 38 | \Swoft\Whoops\WhoopsMiddleware::class, 39 | ], 40 | ], 41 | ``` 42 | 43 | > 注意: 请将 `Swoft\Whoops\WhoopsMiddleware` 加到第一个位置 44 | 45 | ### 异常处理里使用 46 | 47 | 我们稍微调整一下默认的http异常处理类(`App\Exception\Handler\HttpExceptionHandler`): 48 | 49 | ```php 50 | getRequest(); 84 | if ($request->getUriPath() === '/favicon.ico') { 85 | return $response->withStatus(404); 86 | } 87 | 88 | // Log 89 | CLog::error($e->getMessage()); 90 | 91 | // Debug is false 92 | if (!APP_DEBUG) { 93 | return $response 94 | ->withStatus(500) 95 | ->withContent($e->getMessage()); 96 | } 97 | 98 | // Debug is true 99 | $whoops = bean(WhoopsHandler::class); 100 | $content = $whoops->run($e, $request); 101 | return $response->withContent($content); 102 | } 103 | } 104 | ``` 105 | 106 | 现在我们重启 http server,当出现异常时就可以看到由 `whoops` 渲染的错误信息页面了。 107 | 108 | ## 参与贡献 109 | 110 | 欢迎参与贡献,您可以 111 | 112 | - fork 我们的开发仓库 [swoft/whoops](https://github.com/swoft-cloud/swoft-whoops) 113 | - 修改代码然后发起 PR 114 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 115 | -------------------------------------------------------------------------------- /zh-CN/bean/interface.md: -------------------------------------------------------------------------------- 1 | # 接口注入 2 | 3 | 在实际业务场景中,很多情况需要面向接口编程,需要根据接口类名,注入接口实际实现的对象。面向接口编程可以很方便的解耦业务,如下以发送短信为例, 4 | 5 | 6 | > 2.0.3 开始支持 7 | 8 | ## 注解注入 9 | 10 | ```php 11 | use Swoft\Bean\Annotation\Mapping\Bean; 12 | use Swoft\Bean\Annotation\Mapping\Inject; 13 | use Swoft\Bean\Annotation\Mapping\Primary; 14 | use Swoft\Bean\BeanFactory; 15 | 16 | interface SmsInterface 17 | { 18 | public function send(string $content): bool; 19 | } 20 | 21 | 22 | /** 23 | * Class AliyunSms 24 | * 25 | * @since 2.0 26 | * 27 | * @Bean() 28 | * @Primary() 29 | */ 30 | class AliyunSms implements SmsInterface 31 | { 32 | /** 33 | * @param string $content 34 | * 35 | * @return bool 36 | */ 37 | public function send(string $content): bool 38 | { 39 | return true; 40 | } 41 | } 42 | 43 | /** 44 | * Class QcloudSms 45 | * 46 | * @since 2.0 47 | * 48 | * @Bean() 49 | */ 50 | class QcloudSms implements SmsInterface 51 | { 52 | /** 53 | * @param string $content 54 | * 55 | * @return bool 56 | */ 57 | public function send(string $content): bool 58 | { 59 | return true; 60 | } 61 | } 62 | 63 | /** 64 | * Class Sms 65 | * 66 | * @since 2.0 67 | * 68 | * @Bean() 69 | */ 70 | class Sms implements SmsInterface 71 | { 72 | /** 73 | * @Inject() 74 | * 75 | * @var SmsInterface 76 | */ 77 | private $smsInterface; 78 | 79 | /** 80 | * @param string $content 81 | * 82 | * @return bool 83 | */ 84 | public function send(string $content): bool 85 | { 86 | return $this->smsInterface->send($content); 87 | } 88 | } 89 | 90 | /* @var SmsInterface $sms*/ 91 | $sms = BeanFactory::getBean(Sms::class); 92 | 93 | // Send Aliyun sms 94 | $sms->send('sms content'); 95 | ``` 96 | 97 | 当前是以发送阿里云短信为例,如果业务后续变化,需要使用 Gcloud 送短信,只需移除 `AliyunSms` 的 `@Primary()`注解,把此注解加到 `Qcloud` 类上面,完全解耦业务代码,无任何修改。 98 | 99 | > `@Inject()` 注入 bean 名称为空,会优先注入 `@Primary()` 注解标记的实例对象,如果不为空比如 `@Inject(QcloudSms::class)` 就会注入指定的实例对象。 100 | 101 | ### @Primary 102 | 103 | 如果一个接口有多个实现,此标记就是制定接口注入时使用的对象,且同一个接口的多个实现类中只能有一个此注解标记,不然启动会提示错误。 104 | 105 | ## 配置注入 106 | 107 | bean 配置 (app\bean.php) 也是支持接口注入的,如上例子把发送短信的 `Sms` 修改下不用注解,其它不变,代码如下: 108 | 109 | ```php 110 | /** 111 | * Class Sms 112 | * 113 | * @since 2.0 114 | */ 115 | class Sms implements SmsInterface 116 | { 117 | /** 118 | * @var SmsInterface 119 | */ 120 | private $smsInterface; 121 | 122 | /** 123 | * @param string $content 124 | * 125 | * @return bool 126 | */ 127 | public function send(string $content): bool 128 | { 129 | return $this->smsInterface->send($content); 130 | } 131 | } 132 | ``` 133 | 134 | app\bean.php 文件中配置bean 135 | 136 | ```php 137 | [ 138 | Sms::class => [ 139 | 'smsInterface' => bean(SmsInterface::class) 140 | ] 141 | ] 142 | ``` 143 | 144 | 使用代码保存不变,其实现的功能与注解方式是一样的 145 | 146 | ```php 147 | /* @var SmsInterface $sms*/ 148 | $sms = BeanFactory::getBean(Sms::class); 149 | 150 | // Send Aliyun sms 151 | $sms->send('sms content'); 152 | ``` 153 | -------------------------------------------------------------------------------- /zh-CN/extra/apollo.md: -------------------------------------------------------------------------------- 1 | # Apollo 2 | 3 | Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。 4 | Swoft 基于 Apollo 提供的 API,在之上进行封装,使之能在 Swoft 中快速使用。 5 | 6 | ## 安装 7 | 8 | ```php 9 | composer require swoft/apollo 10 | ``` 11 | 12 | ## 配置bean 13 | 14 | 下面以拉取 Apollo 命令空间为 `application` 配置为例: 15 | 16 | 首先在 `app/bean.php` 配置启用 Apollo 17 | 18 | ```php 19 | return [ 20 | // ... 21 | 'apollo' => [ 22 | 'host' => '192.168.2.102', 23 | 'timeout' => 6 24 | ] 25 | 26 | // ... 27 | ]; 28 | ``` 29 | 30 | ### 参数详解 31 | 32 | - host 地址 33 | - port 端口号 34 | - appId Apollo 应用ID 35 | - clusterName Apollo 集群名称 36 | - timeout 超时时间单位秒 37 | 38 |

如果使用 apollo 监听更新回调函数,超时时间必须大于 60 秒

39 | 40 | Apollo 配置完成后,像一个普通的 Bean 一样注入 Apollo `Swoft\Apollo\Config` 即可使用 41 | 42 | ## 使用 43 | 44 | ```php 45 | config->pull('application'); 77 | 78 | // Print data 79 | var_dump($data); 80 | } 81 | } 82 | ``` 83 | 84 | 以上就是一个简单的 Apollo 配置拉取,swoft-apollo 除此方法外,还提供了更多的使用方法。 85 | 86 | ## 方法列表 87 | 88 | ### pullWithCache 89 | 90 | ```php 91 | public function pullWithCache(string $namespace, string $clientIp = ''): array 92 | ``` 93 | 94 | 从 Apollo 缓存中拉取配置,大概有1秒的延迟 95 | 96 | 97 | - $namespace 命名空间名称 98 | - $clientIp 客户端IP,为空底层自动获取当前机器IP,用于灰度发布 99 | 100 | ### pull 101 | 102 | ```php 103 | public function pull(string $namespace, string $releaseKey = '', string $clientIp = ''): array 104 | ``` 105 | 106 | 实时拉取配置,没有延迟时间 107 | 108 | - $namespace 命名空间名称 109 | - $releaseKey 上一次拉取返回的版本号,更多描述,参考 Apollo 官方文档 110 | - $clientIp 客户端IP,为空底层自动获取当前机器IP,用于灰度发布 111 | 112 | ### batchPull 113 | 114 | ```php 115 | public function batchPull(array $namespaces, string $clientIp = ''): array 116 | ``` 117 | 此方法是 `pull` 方法的一个批量封装,用于同时拉取多个 namespace 配置,但是不支持 `$releaseKey` 参数 118 | 119 | - $namespaces 命名空间名称集合数组 120 | - $clientIp 客户端IP,为空底层自动获取当前机器IP,用于灰度发布 121 | 122 | ### listen 123 | 124 | ```php 125 | public function listen(array $namespaces, $callback, array $notifications = [], string $clientIp = ''): void 126 | ``` 127 | 128 | 如果一直轮询拉取配置,会有很大一部分资源浪费,此方法就是解决该问题,当配置有更新的时候,会回调监听改变的函数 129 | 130 | - $namespaces 命名空间名称集合数组 131 | - $callback 配置改变监听函数,可以使闭包、对象方法都可以。 132 | - $notifications 监听信息,详细描述参考 Apollo 官放文档 133 | - $clientIp 客户端IP,为空底层自动获取当前机器IP,用于灰度发布 134 | 135 | 136 | 如果配置改变,监听函数回调的时候会传递一个参数,此参数信息记录了改变后的最新配置信息,比如: 137 | 138 | ```php 139 | $callback = function(array $data){ 140 | // ... 141 | } 142 | ``` 143 | -------------------------------------------------------------------------------- /zh-CN/rpc.md: -------------------------------------------------------------------------------- 1 | # RPC 组件 2 | 3 | ## RPC 基础 4 | 5 | 这里有张很好的图, 以比较复杂的 grpc 来讲解整个 rpc 的流程: 6 | 7 | ![grpc 详解](http://qiniu.daydaygo.top/20190520103806.png) 8 | 9 | RPC, 远程过程调用, 像 **访问本地函数一样通过网络调用远程提供的服务** 10 | 11 | 从网络通信层面来理解 RPC, 标准的 tcp 通信: 12 | - tcp server 13 | - tcp client 14 | - 自定义 tcp 通信协议, 为什么需要协议, 请参考这篇 [网络通信协议设计](https://wiki.swoole.com/wiki/page/484.html) 15 | 16 | > 这么简单? 是的, 就是这么简单. 17 | 18 | 那为什么还会有 grpc/tars 等各种不同的 rpc 方案呢? 大家其实都是在 **协议设计(包括序列化) + 自动化(IDL, 代码自动生成)** 上下功夫. 19 | 20 | ## swoft 中 rpc 实现 21 | 22 | 对应上面的三部分, swoft 的 rpc 实现包含 3 个组件: 23 | - swoft/rpc: 定义 parse, 用来完成协议的解析 24 | - swoft/rpc-client: rpc client, 基于 swoole 提供的协程 client 25 | - swoft/rpc-server: rpc server, 基于 `swoft/server`, 基于 swoole 提供的协程 server 26 | 27 | 以 swoft 提供的 demo 为例: 28 | 29 | - 配置 rpc server: 30 | 31 | ```php 32 | // app/bean.php 33 | 'rpcServer' => [ 34 | 'class' => ServiceServer::class, 35 | ], 36 | ``` 37 | 38 | - 书写 rpc server 业务代码 39 | 40 | ``` 41 | root@cf880ad548d9 /d/s/swoft-skeleton# tree app/Rpc/ 42 | app/Rpc/ 43 | ├── Lib 44 | │   └── UserInterface.php // 使用 Interface 来抽象协议部分, rpc server 和 rpc client 实现此接口来实现协议 45 | ├── Middleware 46 | │   └── ServiceMiddleware.php // 按需添加中间件, 提供 rpc server 可扩展性 47 | └── Service // rpc server 业务代码部分 48 | ├── UserService.php 49 | ├── UserServiceV2.php 50 | └── big.data 51 | ``` 52 | 53 | - 配置 rpc client: 54 | 55 | ```php 56 | // app/bean.php 57 | 'user' => [ // rpc client 58 | 'class' => ServiceClient::class, 59 | 'host' => '127.0.0.1', 60 | 'port' => 18307, 61 | 'setting' => [ 62 | 'timeout' => 0.5, 63 | 'connect_timeout' => 1.0, 64 | 'write_timeout' => 10.0, 65 | 'read_timeout' => 0.5, 66 | ], 67 | 'packet' => \bean('rpcClientPacket') 68 | ], 69 | 'user.pool' => [ // rpc client 配置到连接池中 70 | 'class' => ServicePool::class, 71 | 'client' => \bean('user') 72 | ], 73 | ``` 74 | 75 | rpc-client 配置了连接池, 协程 client 需要使用连接池(**连接池是标配**), 请参考这篇 [是否可以共用同一个redis/mysql连接](https://wiki.swoole.com/wiki/page/325.html) 76 | 77 | 至于怎么使用连接池, 上面的配置就够了, 是的, 还是这么简单. 78 | 79 | - 书写 rpc client 业务代码: 80 | 81 | `app/Http/Controller/RpcController.php` 下提供了完整的示例, 但核心代码其实只有几行: 82 | 83 | ```php 84 | // 注解 + 依赖注入, 从连接池中获取 rpc client 85 | // 变量类型设置为 UserInterface, 和 rpc server 一致, 这就是协议部分 86 | /** 87 | * @Reference(pool="user.pool") 88 | * 89 | * @var UserInterface 90 | */ 91 | private $userService; 92 | 93 | // 传统实现方式, 对比来看, 就能感受到 注解 的优雅 94 | public function __construct() { 95 | $this->$userService = new xxx(); 96 | } 97 | 98 | // 调用 rpc server 99 | $result = $this->userService->getList(12, 'type'); 100 | $result2 = $this->userService2->getList(12, 'type'); 101 | ``` 102 | 103 | rpc 的简单使用, 到这里就结束了. 下面我们以 grpc 为例, 来看看更高阶一点的用法. 104 | 105 | ## grpc 基础 106 | 107 | 关于 grpc 的基础, 推荐参看这篇 [tech| 再探grpc](https://www.jianshu.com/p/f3221df39e6f) 108 | 109 | 要注意 2 个点: 110 | 111 | - grpc 是基于 http2 协议进行通信的, 底层需要使用 `\Swoole\Http\Server` 112 | - 并不需要完整的代码生成, 只要解决 protobuf 的序列化/反序列, swoft 框架提供了 grpc 协议解析和网络通信能力 113 | 114 | 完整过程, 请回顾上图 **grpc 详解** 115 | 116 | ## swoft 中使用 grpc 117 | 118 | test 119 | -------------------------------------------------------------------------------- /zh-CN/rpc-client/usage.md: -------------------------------------------------------------------------------- 1 | # 如何使用 2 | 3 | ```php 4 | /** 5 | * Class RpcController 6 | * 7 | * @since 2.0 8 | * 9 | * @Controller() 10 | */ 11 | class RpcController 12 | { 13 | /** 14 | * @Reference(pool="user.pool") 15 | * 16 | * @var UserInterface 17 | */ 18 | private $userService; 19 | 20 | /** 21 | * @Reference(pool="user.pool", version="1.2") 22 | * 23 | * @var UserInterface 24 | */ 25 | private $userService2; 26 | 27 | /** 28 | * @RequestMapping("getList") 29 | * 30 | * @return array 31 | */ 32 | public function getList(): array 33 | { 34 | $result = $this->userService->getList(12, 'type'); 35 | $result2 = $this->userService2->getList(12, 'type'); 36 | 37 | return [$result, $result2]; 38 | } 39 | 40 | /** 41 | * @RequestMapping("returnBool") 42 | * 43 | * @return array 44 | */ 45 | public function returnBool(): array 46 | { 47 | $result = $this->userService->delete(12); 48 | 49 | if (is_bool($result)) { 50 | return ['bool']; 51 | } 52 | 53 | return ['notBool']; 54 | } 55 | 56 | /** 57 | * @RequestMapping() 58 | * 59 | * @return array 60 | */ 61 | public function bigString(): array 62 | { 63 | $string = $this->userService->getBigContent(); 64 | 65 | return ['string']; 66 | } 67 | } 68 | ``` 69 | 70 | ## @Reference 71 | 72 | - pool 指定使用那个服务的连接池(使用那个服务) 73 | - version 指定服务的版本 74 | 75 | ## 非 Swoft 框架调用 76 | 77 | 默认消息协议是 json-rpc, 所以我们按照这个格式就可以了,需要注意的是,默认消息协议是以 `\r\n\r\n` 结尾的。 78 | 79 | 这里 `method` 的格式为 `"{version}::{class_name}::{method_name}"` 80 | 81 | ```json 82 | { 83 | "jsonrpc": "2.0", 84 | "method": "{version}::{class_name}::{method_name}", 85 | "params": [], 86 | "id": "", 87 | "ext": [] 88 | } 89 | ``` 90 | 91 | ### 代码示例 92 | 93 | 如果使用默认消息协议,可以按照如下方式进行封装 94 | 95 | ```php 96 | '2.0', 108 | "method" => sprintf("%s::%s::%s", $version, $class, $method), 109 | 'params' => $param, 110 | 'id' => '', 111 | 'ext' => $ext, 112 | ]; 113 | $data = json_encode($req) . RPC_EOL; 114 | fwrite($fp, $data); 115 | 116 | $result = ''; 117 | while (!feof($fp)) { 118 | $tmp = stream_socket_recvfrom($fp, 1024); 119 | 120 | if ($pos = strpos($tmp, RPC_EOL)) { 121 | $result .= substr($tmp, 0, $pos); 122 | break; 123 | } else { 124 | $result .= $tmp; 125 | } 126 | } 127 | 128 | fclose($fp); 129 | return json_decode($result, true); 130 | } 131 | 132 | $ret = request('tcp://127.0.0.1:18307', \App\Rpc\Lib\UserInterface::class, 'getList', [1, 2], "1.0"); 133 | 134 | var_dump($ret); 135 | ``` 136 | -------------------------------------------------------------------------------- /zh-CN/rpc-server/statement.md: -------------------------------------------------------------------------------- 1 | # 声明服务 2 | 3 | # 接口服务 4 | 5 | 定义接口并实现接口,才能提供RPC服务。 6 | 7 | ## 目录定义 8 | 9 | 官方应用中给出的目录如下: 10 | 11 | ```text 12 | app/ 13 | Rpc/ 14 | - Lib/ // 服务的公共接口定义目录,里面通常只有php接口类 15 | - Services/ // 具体的服务接口实现类,里面的类通常实现了 Lib 中定义的接口 16 | ``` 17 | 18 | > 当然在多个服务中使用时, 要将lib库 `app/Rpc/Lib` 移到一个公共的git仓库里,然后各个服务通过 composer 来获取使用 19 | 20 | ## 定义接口 21 | 22 | 服务提供方定义好接口格式,存放到公共的lib库里面,服务调用方,加载lib库,就能使用接口服务,接口定义和普通接口完全一致。 23 | 24 | ```php 25 | ** 26 | * Class UserInterface 27 | * 28 | * @since 2.0 29 | */ 30 | interface UserInterface 31 | { 32 | /** 33 | * @param int $id 34 | * @param mixed $type 35 | * @param int $count 36 | * 37 | * @return array 38 | */ 39 | public function getList(int $id, $type, int $count = 10): array; 40 | 41 | /** 42 | * @param int $id 43 | * 44 | * @return bool 45 | */ 46 | public function delete(int $id): bool; 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getBigContent(): string; 52 | } 53 | ``` 54 | 55 | ## 接口实现 56 | 57 | 一个接口,会存在多种不同的实现,通过一个版本号来标识是那个逻辑实现。 58 | 59 | ### 注解 60 | 61 | **@Service** 62 | 63 | - version 定义接口版本,默认是 `1.0` 64 | 65 | ### 实例 66 | 67 | **实现版本1** 68 | 69 | ```php 70 | /** 71 | * Class UserService 72 | * 73 | * @since 2.0 74 | * 75 | * @Service() 76 | */ 77 | class UserService implements UserInterface 78 | { 79 | /** 80 | * @param int $id 81 | * @param mixed $type 82 | * @param int $count 83 | * 84 | * @return array 85 | */ 86 | public function getList(int $id, $type, int $count = 10): array 87 | { 88 | return ['name' => ['list']]; 89 | } 90 | 91 | /** 92 | * @param int $id 93 | * 94 | * @return bool 95 | */ 96 | public function delete(int $id): bool 97 | { 98 | return false; 99 | } 100 | 101 | /** 102 | * @return string 103 | */ 104 | public function getBigContent(): string 105 | { 106 | $content = Co::readFile(__DIR__ . '/big.data'); 107 | return $content; 108 | } 109 | } 110 | ``` 111 | 112 | **实现版本2** 113 | 114 | ```php 115 | /** 116 | * Class UserServiceV2 117 | * 118 | * @since 2.0 119 | * 120 | * @Service(version="1.2") 121 | */ 122 | class UserServiceV2 implements UserInterface 123 | { 124 | /** 125 | * @param int $id 126 | * @param mixed $type 127 | * @param int $count 128 | * 129 | * @return array 130 | */ 131 | public function getList(int $id, $type, int $count = 10): array 132 | { 133 | return [ 134 | 'name' => ['list'], 135 | 'v' => '1.2' 136 | ]; 137 | } 138 | 139 | /** 140 | * @param int $id 141 | * 142 | * @return bool 143 | */ 144 | public function delete(int $id): bool 145 | { 146 | return false; 147 | } 148 | 149 | /** 150 | * @return string 151 | */ 152 | public function getBigContent(): string 153 | { 154 | $content = Co::readFile(__DIR__ . '/big.data'); 155 | return $content; 156 | } 157 | } 158 | ``` 159 | 160 | > 不同的实现,需要定义**不同的唯一版本号**,如果存在相同,加载之后的服务会覆盖之前的服务 161 | 162 | -------------------------------------------------------------------------------- /zh-CN/event/swoole-events.md: -------------------------------------------------------------------------------- 1 | # swoole 事件 2 | 3 | swoole文档上列出的每个事件,在swoft里面都可以监听,并且可以有多个监听器。 4 | 5 | ## 事件常量 6 | 7 | swoft 里我们为这些事件名添加了常量,方便统一使用和管理。 8 | 9 | ```php 10 | 可用自 `>= v2.0.5` 6 | 7 | ## 安装 8 | 9 | 使用定时任务前,必须安装,安装如下 10 | 11 | ```php 12 | composer require swoft/crontab 13 | ``` 14 | 15 | ## 注解 16 | 17 | 在 Swoft 中,定时任务的使用非常的简单,只需要使用相关注解定义你的任务类即可。 18 | 19 | **@Scheduled()** 20 | 21 | 用于声明定时任务,如果是声明定时任务类,则必须使用此注解 22 | 23 | 参数 24 | 25 | * `name` 任务类的名称,为空则为此类的完整 `namespace`路径。 26 | 27 | 使用示例:`@Scheduled()`、`@Scheduled("taskName")`、`@Scheduled(name="taskName")` 28 | 29 | **@Cron()** 30 | 31 | 声明需要运行的方法,如果没有使用此注解,则该方法不会被运行。 32 | 33 | 参数 34 | 35 | * `value` 任务的 Crontab 表达式,支持到秒 36 | 37 | 使用示例: `@Cron("* * * * * *")`、`@Cron(value="* * * * * *")`,表达式可简写,例如一个每秒都要执行的任务则可定义为 `@Cron("*")` 38 | 39 | ## Cron格式说明 40 | 41 | ``` 42 | * * * * * * 43 | - - - - - - 44 | | | | | | | 45 | | | | | | +----- day of week (0 - 6) (Sunday=0) 46 | | | | | +----- month (1 - 12) 47 | | | | +------- day of month (1 - 31) 48 | | | +--------- hour (0 - 23) 49 | | +----------- min (0 - 59) 50 | +------------- sec (0-59) 51 | ``` 52 | 53 | 示例: 54 | 55 | 1. `* * * * * *` 表示每秒执行一次。 56 | 2. `0 * * * * *` 表示每分钟的第0秒执行一次,即每分钟执行一次。 57 | 3. `0 0 * * * *` 表示每小时的0分0秒执行一次,即每小时执行一次。 58 | 4. `0/10 * * * * *` 表示每分钟的第0秒开始每10秒执行一次。 59 | 5. `10-20 * * * * *` 表示每分钟的第10-20秒执行一次。 60 | 6. `10,20,30 * * * * *` 表示每分钟的第10,20,30秒各执行一次。 61 | 62 | ## 声明定时任务 63 | 64 | 在 Swoft 中使用定时任务相当的简单,只需两步操作,`声明定时任务`和`配置启用`,这两部操作都相当的简单,我们先来看声明任务。 65 | 66 | ```php 67 | [ 109 | // ... 110 | 'process' => [ 111 | 'crontab' => bean(Swoft\Crontab\Process\CrontabProcess::class) 112 | ], 113 | // ... 114 | ], 115 | ]; 116 | ``` 117 | 如上我们就配置成功了服务启动后,我们的定时任务进程也会随之启动 118 | 119 | ## 手动执行 120 | 121 | 除了定时执行我们设置好的任务外,我们还可以在业务代码中直接手动执行我们的定时任务,方法如下。 122 | 123 | ```php 124 | $crontab = BeanFactory::getBean("crontab"); 125 | $crontab->execute("testCrontab", "method"); 126 | ``` 127 | 128 | 通过 Bean 容器拿到 crontab 管理器,然后直接使用 `execute($beanName,$methodName)` 方法,此方法有两个参数,`$beanName` 就是传入在 `@Scheduled()` 注解中设置的名字,`$methodName` 则是传入 `@Scheduled()` 标注的类中,`@Cron()` 标注的方法。 129 | -------------------------------------------------------------------------------- /zh-CN/process/user-process.md: -------------------------------------------------------------------------------- 1 | # 用户进程 2 | 3 | Http/RPC/Websocket/TCP 等服务有些业务场景,需要一个后台运行进程去监控、上报或者其它特殊操作,此时可以在相应服务启动的时候,添加一个用户自定义工作进程,来实现。 4 | 自定义用户进程与服务一起启动,服务关闭一起退出,如果自定义用户进程被意外关闭,服务会重新启动一个新的自定义用户进程,保证自定义用户进程一直存在。 5 | 6 | > 2.0.4+ 支持且需要安装 [swoft-process](index.md) 组件 7 | 8 | ## 声明用户进程 9 | 10 | 使用自定义用户进程之前,必须定义用户进程,如下定义一个监控上报信息的用户进程为例: 11 | 12 | 13 | 自定义用户进程入口: 14 | 15 | ```php 16 | logic->monitor($process); 53 | } 54 | } 55 | ``` 56 | 57 | - 自定义用户进程必须实现 `Swoft\Process\UserProcess` 接口 58 | - 自定义用户进程必须使用 `@Bean` 标记为一个 bean 对象 59 | 60 | 61 | 业务处理: 62 | 63 | ```php 64 | name('swoft-monitor'); 94 | 95 | while (true) { 96 | $connections = context()->getServer()->getSwooleServer()->connections; 97 | CLog::info('monitor = ' . json_encode($connections)); 98 | 99 | // Database 100 | $user = User::find(1)->toArray(); 101 | CLog::info('user='.json_encode($user)); 102 | 103 | // Redis 104 | Redis::set('test', 'ok'); 105 | CLog::info('test='.Redis::get('test')); 106 | 107 | Coroutine::sleep(3); 108 | } 109 | } 110 | } 111 | ``` 112 | 113 |

自定义用户进程里面,开发者必须实现类似 `while(true)` 的业务,且里面可以直接使用 Swoft 封装好的所有IO操作,比如数据库、缓存、RPC,以及其它非 Swoft 封装的协程操作

114 | 115 | ## 配置 116 | 117 | 定义好了用户进程,必须配置才会有效,Http/RPC/Websocket/TCP 服务配置自定义进程都一样,这里以如上自定义的用户进程配置为例: 118 | 119 | 配置自定义进程: 120 | 121 | ```php 122 | return [ 123 | 'httpServer' => [ 124 | 'class' => HttpServer::class, 125 | 'port' => 18306, 126 | 'listener' => [ 127 | 'rpc' => bean('rpcServer') 128 | ], 129 | 'process' => [ 130 | 'monitor' => bean(MonitorProcess::class) 131 | ], 132 | // ... 133 | ], 134 | ]; 135 | ``` 136 | 137 | 这里以注入的方式配置了一个自定义用户进程,名称为 `monitor` 138 | 139 | 140 | > 如果配置成功,服务启动后,用户进程里面的业务会自动执行,无需其它操作。 -------------------------------------------------------------------------------- /zh-CN/ms/govern/config.md: -------------------------------------------------------------------------------- 1 | # 配置中心 2 | 3 | 配置中心主要就是把配置集中化管理,方便统一和维护。本章以 Apollo 为例,从远端配置中心拉取配置以及安全重启服务。如果对 Apollo 不熟悉,可以先看 4 | Swoft 扩展 Apollo 组件以及阅读 Apollo 官方文档。 5 | 6 | 配置中心使用流程 7 | 8 | - 编写本地 agent 监听配置的变化,如果有变更,修改本地配置文件 9 | - 重启业务对应的服务 10 | 11 | > 本地 agent 一定要比服务先启动,否则服务启动,没法获取最新的配置信息 12 | 13 | ## 使用 14 | 15 | 本章以 Swoft 中使用 apollo 为例,当 apollo 配置变更后,重启服务(http-server / rpc-server/ ws-server)。如下是一个 agent 例子: 16 | 17 | 18 | ### 声明 agent 19 | 20 | ```php 21 | config->listen($namespaces, [$this, 'updateConfigFile']); 67 | } catch (Throwable $e) { 68 | CLog::error('Config agent fail(%s %s %d)!', $e->getMessage(), $e->getFile(), $e->getLine()); 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * @param array $data 75 | * 76 | * @throws ContainerException 77 | * @throws ReflectionException 78 | */ 79 | public function updateConfigFile(array $data): void 80 | { 81 | foreach ($data as $namespace => $namespaceData) { 82 | $configFile = sprintf('@config/%s.php', $namespace); 83 | 84 | $configKVs = $namespaceData['configurations'] ?? ''; 85 | $content = 'restart(); 93 | 94 | // /** @var ServiceServer $server */ 95 | // $server = bean('rpcServer'); 96 | // $server->restart(); 97 | 98 | // /* @var WebSocketServer $server */ 99 | // $server = bean('wsServer'); 100 | // $server->restart(); 101 | } 102 | } 103 | } 104 | ``` 105 | 106 | 这里声明了一个本地 agent,它监听 apollo 远程配置变更,如果有变更,回调函数,同时返回最新的配置信息,根据业务实际情况重新修改配置文件,再重启相应的服务 107 | 108 | ### 启动 agent 109 | 110 | ``` 111 | php bin/swoft agent:index 112 | ``` 113 | 114 | > agent 启动,可以后台守护进程启动,防止挂掉 115 | 116 | ### 启动服务 117 | 118 | 以启动 Http server 为例 119 | 120 | ``` 121 | /usr/local/php/bin/php /data/www/swoft/bin/swoft http:start -d 122 | ``` 123 | 124 |

启动命令的路径,必须是绝对路径 (/data/www/swoft/bin/swoft) 且后台运行模式 (-d),服务的启动必须在 agent 之后,否则无法获取最新配置信息。本章只是一个简单的使用例子,开发者可以更加自己的实际业务情况,监听 apollo 配置变更,生成配置文件,重启服务

-------------------------------------------------------------------------------- /zh-CN/common/co.md: -------------------------------------------------------------------------------- 1 | # 协程函数 2 | 3 | 框架中封装了一些协程操作的函数,方便开发者调用 4 | 5 | ## 创建协程 6 |

Swoft 框架中一定不要直接使用 Swoole 提供的 `go` 函数创建协程,否则会导致请求和上下文丢失导致一些奇葩问题。

7 | 8 | ``` 9 | use Swoft\Co; 10 | 11 | Co::create(function(){ 12 | // to do 13 | }); 14 | ``` 15 | 16 | 如上是框架中创建协程的原型,框架也提供了更为简单的 `sgo` 函数来创建协程,替换 Swoole 的 `go` 函数使用。 17 | 18 | ```php 19 | sgo(function(){ 20 | // todo 21 | }); 22 | ``` 23 | 24 |

`sgo` 函数使用和 Swoole `go` 函数完全一样,切记框架中只能使用 `sgo` 函数,不能直接使用 `go` 函数

25 | 26 | 27 | ## 协程ID 28 | 29 | 获取当前协程ID, -1 非协程环境 30 | 31 | ```php 32 | use Swoft\Co; 33 | 34 | $id = Co::id(); 35 | ``` 36 | 37 | ## 顶级协程ID 38 | 39 | 获取顶级(最外层协程ID) 40 | 41 | ```php 42 | use Swoft\Co; 43 | 44 | $id = Co::tid(); 45 | ``` 46 | 47 | ## 读取文件 48 | 49 | 全量读取文件 50 | 51 | ```php 52 | public static function readFile(string $filename): string 53 | ``` 54 | 55 | - 读取的文件路径 56 | 57 | 读取成功返回字符串内容,读取失败返回 false,可使用swoole_last_error获取错误信息。readFile方法没有尺寸限制,读取的内容会存放在内存中,因此读取超大文件时可能会占用过多内存 58 | 59 | 使用实例: 60 | 61 | ```php 62 | use Swoft\Co; 63 | 64 | $fileName = 'test.file'; 65 | $data = Co::readFile($fileName); 66 | ``` 67 | 68 | ## 写文件 69 | 70 | ```php 71 | public static function writeFile(string $filename, string $data, int $flags = null): int 72 | ``` 73 | 74 | - filename为文件的名称,必须有可写权限,文件不存在会自动创建。打开文件失败会立即返回false 75 | - $fileContent为要写入到文件的内容,最大可写入4M 76 | - flags为写入的选项,默认会清空当前文件内容,可以使用FILE_APPEND表示追加到文件末尾 77 | 78 | 写入成功返回true,写入失败返回false 79 | 80 | ```php 81 | use Swoft\Co; 82 | 83 | $fileName = 'test.file'; 84 | $data = Co::writeFile($fileName, 'data'); 85 | ``` 86 | 87 | 88 | ## 并发 89 | 90 | 框架底层通过协程通道,封装了一套多混合IO并发操作的方法,一般用于多个流程没有依赖,可以并发执行,提高执行效率。 91 | 92 | ```php 93 | public static function multi(array $requests, float $timeout = 0): array 94 | ``` 95 | 96 | - requests 多个操作集合,KV 数组格式 97 | - timeout 超时时间,默认永久超时 98 | 99 | 并发执行结果,按照 requests 集合数组 key 对应关系返回,如果一个 key 对应的值返回 false, 意味着该操作执行失败. requests 里面的每个操作可以执行的业务无上限,根据自己的业务而定。 100 | 101 | requests 格式支持多种方式 102 | 103 | - 对象的某个方法 104 | - 对象的静态方法 105 | - 闭包匿名函数 106 | 107 | 使用实例如下: 108 | 109 | ```php 110 | use Swoft\Co; 111 | 112 | /** 113 | * Class CoTest 114 | * 115 | * @since 2.0 116 | */ 117 | class CoTest 118 | { 119 | /** 120 | * @throws \ReflectionException 121 | * @throws \Swoft\Bean\Exception\ContainerException 122 | */ 123 | public function testMulti() 124 | { 125 | $requests = [ 126 | 'method' => [$this, 'requestMethod'], 127 | 'staticMethod' => "SwoftTest\Unit\CoTest::requestMehtodByStatic", 128 | 'closure' => function () { 129 | $cli = new Client('www.baidu.com', 80); 130 | $cli->get('/'); 131 | $result = $cli->body; 132 | $cli->close(); 133 | 134 | return $result; 135 | } 136 | ]; 137 | 138 | $response = Co::multi($requests); 139 | } 140 | 141 | /** 142 | * @return mixed 143 | */ 144 | public function requestMethod() 145 | { 146 | $cli = new Client('www.baidu.com', 80); 147 | $cli->get('/'); 148 | $result = $cli->body; 149 | $cli->close(); 150 | 151 | return $result; 152 | } 153 | 154 | /** 155 | * @return mixed 156 | */ 157 | public static function requestMehtodByStatic() 158 | { 159 | $cli = new Client('www.baidu.com', 80); 160 | $cli->get('/'); 161 | $result = $cli->body; 162 | $cli->close(); 163 | 164 | return $result; 165 | } 166 | } 167 | ``` 168 | -------------------------------------------------------------------------------- /zh-CN/best-practices/nginx-config.md: -------------------------------------------------------------------------------- 1 | # nginx 配置 2 | 3 | 这里列出了一些常用的nginx配置 4 | 5 | ## http 应用 6 | 7 | ```nginx 8 | server { 9 | listen 80; 10 | server_name www.site.dev site.dev; 11 | root /path/to/your-project/public; 12 | index index.html index.htm; 13 | 14 | error_log logs/site.dev.error.log; 15 | access_log logs/site.dev.access.log; 16 | 17 | ##### 第一个必选规则: 匹配首页 18 | location = / { 19 | proxy_pass http://127.0.0.1:18306; 20 | } 21 | 22 | ##### 第二个必选规则: 处理静态文件请求,这是nginx作为http服务器的强项 23 | # 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用 24 | # location ^~ /static/ { 25 | # root /path/to/your-project/static/; 26 | # } 27 | 28 | location ~* \.(js|css|map|png|jpg|jpeg|gif|ico|ttf|woff2|woff)$ { 29 | expires max; 30 | # root /path/to/your-project/static/; 31 | # log_not_found off; 32 | access_log off; 33 | } 34 | 35 | ##### 通用规则: 上面的都不匹配 36 | location / { 37 | # try_files $uri $uri/; 38 | 39 | # proxy_redirect off; 40 | proxy_set_header X-Real-IP $remote_addr; 41 | proxy_set_header Host $host; 42 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 43 | proxy_http_version 1.1; 44 | # proxy_set_header Upgrade $http_upgrade; 45 | # proxy_set_header Connection "upgrade"; 46 | proxy_set_header Connection "keep-alive"; 47 | 48 | # 没有找到文件就转发到 swoole server 49 | # 也可去掉 if. 全部转发到后端server 50 | if (!-e $request_filename){ 51 | proxy_pass http://127.0.0.1:18306; 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | ## websocket 应用 58 | 59 | ```conf 60 | server { 61 | listen 80; 62 | server_name io.yourhost.com; 63 | 64 | location / { 65 | proxy_set_header Upgrade $http_upgrade; 66 | proxy_set_header Connection "upgrade"; 67 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 68 | proxy_set_header Host $host; 69 | proxy_http_version 1.1; 70 | proxy_pass http://127.0.0.1:18307; 71 | } 72 | } 73 | ``` 74 | 75 | ## 启用 https 版本 76 | 77 | ```nginx 78 | server { 79 | listen 443 ssl; 80 | listen 80; 81 | server_name swoft.org www.swoft.org; 82 | root /path/to/your-project/public; 83 | index index.html index.htm; 84 | 85 | ssl on; 86 | ssl_certificate /my-certs/2235215_www.swoft.org.pem; 87 | ssl_certificate_key /my-certs/2235215_www.swoft.org.key; 88 | 89 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 90 | ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; 91 | ssl_session_cache shared:SSL:20m; 92 | ssl_session_timeout 60m; 93 | ssl_prefer_server_ciphers on; 94 | 95 | add_header Strict-Transport-Security max-age=16000000; 96 | # error_page 497 https://$host$request_uri; 97 | 98 | error_log /var/log/nginx/your-project.error.log; 99 | access_log /var/log/nginx/your-project.access.log; 100 | 101 | location / { 102 | # proxy_redirect off; 103 | proxy_set_header X-Real-IP $remote_addr; 104 | proxy_set_header Host $host; 105 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 106 | proxy_http_version 1.1; 107 | # proxy_set_header Upgrade $http_upgrade; 108 | # proxy_set_header Connection "upgrade"; 109 | proxy_set_header Connection "keep-alive"; 110 | 111 | proxy_pass http://127.0.0.1:18308; 112 | } 113 | 114 | location ~* \.(js|map|css|png|jpg|jpeg|gif|ico|ttf|woff2|woff)$ { 115 | expires max; 116 | #log_not_found off; 117 | access_log off; 118 | } 119 | } 120 | ``` 121 | -------------------------------------------------------------------------------- /zh-CN/websocket-server/exception.md: -------------------------------------------------------------------------------- 1 | # 异常处理 2 | 3 | 前面我们了解了系统如何处理异常,以及http server里如何处理异常的。在websocket server 也是类似的,我们只需定义websocket相关场景的异常处理器就行。 4 | 5 | 与http server 里只有一个 request 场景不同, websocket 里有四个场景: 6 | 7 | - `handshake` 握手环节 8 | - `open` 握手后连接打开 9 | - `message` 消息通信阶段 10 | - `close` 连接关闭 11 | 12 | 下面我们编写 websocket 几个环节中最重要的 握手 和 消息通信 环节的异常处理。其他环节的可以参考和继承相关类来编写。 13 | 14 | ## 握手异常 15 | 16 | 因为websocket握手环节就是http请求处理,所以此环节的异常跟http里处理是一样的,当然你还是得继承为这个场景设计的基础类才行。 17 | 18 | - 必须继承 `AbstractHandshakeErrorHandler` 类,我们才能知道你要处理哪个场景里的异常 19 | 20 | ```php 21 | withStatus(500)->withContent(sprintf( 55 | '%s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine() 56 | )); 57 | } 58 | 59 | $data = [ 60 | 'code' => $e->getCode(), 61 | 'error' => sprintf('(%s) %s', get_class($e), $e->getMessage()), 62 | 'file' => sprintf('At %s line %d', $e->getFile(), $e->getLine()), 63 | 'trace' => $e->getTraceAsString(), 64 | ]; 65 | 66 | // Debug is true 67 | return $response->withData($data); 68 | } 69 | } 70 | ``` 71 | 72 | ## 消息通信异常 73 | 74 | 在握手成功后的消息通信阶段出现异常,也可以方便的捕获处理。 75 | 76 | > 注意:你仍然需要继承专有场景的异常处理抽象类 `AbstractMessageErrorHandler` 77 | 78 | ```php 79 | getMessage(), $e->getFile(), $e->getLine()); 112 | 113 | Log::error('Ws server error(%s)', $message); 114 | 115 | // Debug is false 116 | if (!APP_DEBUG) { 117 | server()->push($frame->fd, $e->getMessage()); 118 | return; 119 | } 120 | 121 | server()->push($frame->fd, $message); 122 | } 123 | } 124 | ``` 125 | 126 | -------------------------------------------------------------------------------- /zh-CN/event/usage.md: -------------------------------------------------------------------------------- 1 | # 事件注册 2 | 3 | swoft提供了简便的事件管理和使用 4 | 5 | ## 注解 6 | 7 | ### Listener 8 | 9 | 事件监听器类注解tag `@Listener` 10 | 11 | - 注解类: `Swoft\Event\Annotation\Mapping\Listener` 12 | - 作用范围: `CLASS` 13 | - 拥有属性: 14 | + `event` _string_ 要监听的事件名称 15 | + `priority` _int_ 此监听器的优先级,值越大越先被调用 16 | 17 | > 注意你的类必须实现接口: `Swoft\Event\EventHandlerInterface` 18 | 19 | ### Subscriber 20 | 21 | 事件监听器类注解tag `@Subscriber`,与 `@Listener` 不同的是,允许在同一个类里处理多个事件。 22 | 23 | - 注解类: `Swoft\Event\Annotation\Mapping\Subscriber` 24 | - 作用范围: `CLASS` 25 | 26 | > 注意你的类必须实现接口: `Swoft\Event\EventSubscriberInterface` 27 | 28 | ## 示例 29 | 30 | ### Listener示例 31 | 32 | ```php 33 | getName()}' on the: $pos\n"; 54 | } 55 | } 56 | ``` 57 | 58 | ### Subscriber示例 59 | 60 | ```php 61 | 'handler method' 84 | * 'event name' => ['handler method', priority] 85 | * ] 86 | */ 87 | public static function getSubscribedEvents(): array 88 | { 89 | return [ 90 | self::EVENT_ONE => 'handleEvent1', 91 | self::EVENT_TWO => ['handleEvent2', ListenerPriority::HIGH], 92 | ]; 93 | } 94 | 95 | public function handleEvent1(EventInterface $evt): void 96 | { 97 | $evt->setParams(['msg' => 'handle the event: test.event1 position: TestSubscriber.handleEvent1()']); 98 | } 99 | 100 | public function handleEvent2(EventInterface $evt): void 101 | { 102 | $evt->setParams(['msg' => 'handle the event: test.event2 position: TestSubscriber.handleEvent2()']); 103 | } 104 | } 105 | ``` 106 | 107 | ## 触发事件 108 | 109 | > 事件名称管理推荐放置在一个单独类的常量里面,方便管理和维护 110 | 111 | ### 方式一 112 | 113 | 使用简便。但是多个参数按顺序放入,因此获取时需要根据索引获取。 114 | 115 | - `Swoft::trigger('event name', mixd $target, $args...)` 116 | 117 | ```php 118 | \Swoft::trigger('event name', 'target', $arg0, $arg1); 119 | ``` 120 | 121 | 获取事件参数: 122 | 123 | ```php 124 | $target = $event->getTarget(); 125 | 126 | $arg0 = $event->getParam(0); 127 | $arg1 = $event->getParam(1); 128 | ``` 129 | 130 | ### 方式二 131 | 132 | 使用稍微麻烦一点。但是多个参数按k-v放入,获取时可以根据key获取。 133 | 134 | - `Swoft::triggerByArray('event name', mixd $target, array $args)` 135 | 136 | ```php 137 | \Swoft::triggerByArray('event name', 'target', [ 138 | 'arg0' => $arg0, 139 | 'arg0' => $arg1 140 | ]); 141 | ``` 142 | 143 | 获取事件参数: 144 | 145 | ```php 146 | $target = $event->getTarget(); 147 | 148 | $arg0 = $event->getParam('arg0'); 149 | $arg1 = $event->getParam('arg1'); 150 | ``` 151 | -------------------------------------------------------------------------------- /zh-CN/tcp-server/config.md: -------------------------------------------------------------------------------- 1 | # tcp server 配置 2 | 3 | tcp server 的 host, port 等配置是都是完全可以自定义的。 4 | 配置需要编辑 `app/bean.php` 文件,下面列举了一些简单的配置,你也可以自由组合同时提供多种服务。 5 | 6 | > tcp server 的默认端口是 `18309` 7 | 8 | ## 可配置项 9 | 10 | 可配置项用于 `tcpServer` bean 配置,除了 `class` 其他都是 `TcpServer` 的属性。 11 | 12 | - `class` 指定 tcp server 的bean类,默认即是 `Swoft\Tcp\Server\TcpServer::class` 13 | - `port` 指定 tcp server 的端口 14 | - `listener` 指定其他一同启动的服务,添加端口服务监听,可以多个。 15 | - rpc 启动 `RPC` 服务 16 | - `on` 配置监听的事件 17 | - 注册swoole事件、设置对应事件的处理监听 18 | - `setting` 这里是参考 [Swoole Server配置选项](https://wiki.swoole.com/wiki/page/274.html) 19 | - `pidFile` 设置进程 `pid文件` 位置,默认值 `@runtime/swoft-tcp.pid` 20 | - `mode` 运行的模式,参考 [Swoole Server 构造函数](https://wiki.swoole.com/wiki/page/14.html) 第三个参数 21 | - `type` 指定Socket的类型,支持TCP、UDP、TCP6、UDP6、UnixSocket Stream/Dgram 等 [Swoole Server 构造函数](https://wiki.swoole.com/wiki/page/14.html) 第四个参数 22 | 23 | ## 基础配置 24 | 25 | ```php 26 | // ... 27 | 'tcpServer' => [ 28 | 'class' => TcpServer::class, 29 | 'port' => 18309, 30 | 'debug' => env('SWOFT_DEBUG', 0), 31 | /* @see TcpServer::$setting */ 32 | 'setting' => [ 33 | 'log_file' => alias('@runtime/swoole.log'), 34 | ], 35 | ], 36 | /** @see \Swoft\Tcp\Protocol */ 37 | 'tcpServerProtocol' => [ 38 | 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, 39 | // 'openEofCheck' => true, // Defalut use EOF check 40 | // 'openLengthCheck' => true, 41 | ], 42 | ``` 43 | 44 | ### 协议配置 45 | 46 | 通常你只需配置好协议的分包方式,内部的细节配置会自动同步设置到TcpServer。 47 | 48 | ```php 49 | /** @see \Swoft\Tcp\Protocol */ 50 | 'tcpServerProtocol' => [ 51 | 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, 52 | // 'openEofCheck' => true, // Defalut use EOF check 53 | // 'openLengthCheck' => true, 54 | ], 55 | ``` 56 | 57 | 可配置项: 58 | 59 | - `type` _string_ 默认的数据打包器的类型。默认是 `token-text` 60 | - `packers` _array_ 可用的数据打包器的列表,内置了 `json` `php` `token-text` 三种。 61 | - `packageMaxLength` _int_ 同 swoole 的 `package_max_length` 默认 `81920` 62 | - `openEofCheck` _bool_ 同 swoole 的 `open_eof_check` 默认 `true` 63 | - `openLengthCheck` _bool_ 同 swoole 的 `open_length_check`,总是与 `openEofCheck` 相反。默认 `false` 64 | 65 | 66 | ## 添加RPC服务 67 | 68 | 如果你想运行tcp server时,同时启动RPC Server服务。 69 | 70 | ```php 71 | // ... 72 | 'tcpServer' => [ 73 | 'listener' => [ 74 | 'rpc' => \bean('rpcServer') // 引入 rpcServer 75 | ], 76 | ], 77 | 'rpcServer' => [ 78 | 'class' => ServiceServer::class, 79 | 'port' => 18308, 80 | ], 81 | ``` 82 | 83 | ## 启用全部功能 84 | 85 | - task process 86 | - rpc server 87 | 88 | ```php 89 | // ... 90 | 'tcpServer' => [ 91 | 'class' => TcpServer::class, 92 | 'port' => 18307, 93 | 'on' => [ 94 | // 启用任务必须添加 task, finish 事件处理 95 | SwooleEvent::TASK => bean(TaskListener::class), 96 | SwooleEvent::FINISH => bean(FinishListener::class) 97 | ], 98 | 'listener' => [ 99 | // 引入 rpcServer 100 | 'rpc' => \bean('rpcServer') 101 | ], 102 | 'debug' => env('SWOFT_DEBUG', 0), 103 | /* @see TcpServer::$setting */ 104 | 'setting' => [ 105 | 'log_file' => alias('@runtime/swoole.log'), 106 | // 任务需要配置 task worker 107 | 'task_worker_num' => 2, 108 | 'task_enable_coroutine' => true 109 | ], 110 | ], 111 | 'rpcServer' => [ 112 | 'class' => ServiceServer::class, 113 | 'port' => 18308, 114 | ], 115 | ``` 116 | 117 | ok, 现在通过 `php bin/swoft tcp:start` 启动的服务器,就支持上面的全部功能了 118 | 119 | -------------------------------------------------------------------------------- /zh-CN/extra/postgresql.md: -------------------------------------------------------------------------------- 1 | # Swoft PostgreSQL 2 | 3 | `curtis18/swoft-pgsql` 是对 `PostgreSQL` 在 Swoft 中使用的简单封装,支持连接池配置和某些非连接池的原生配置函数。 4 | 5 | ## Github 6 | 7 | - https://github.com/curtis18/swoft-pgsql.git 8 | 9 | ## 安装 10 | 11 | `curtis18/swoft-pgsql` 需要使用 Swoole 的 `swoole/ext-postgresql` 扩展。而作为 Swoft 的一个额外的扩展组件,需要手动安装: 12 | 13 | - 通过 composer 命令: 14 | 15 | ```bash 16 | composer require curtis18/swoft-pgsql 17 | ``` 18 | 19 | - 通过 `composer.json` 配置: 20 | 21 | ```json 22 | "curtis18/swoft-pgsql": "~1.0.1" 23 | ``` 24 | 25 | ## 使用 26 | 27 | ### 基础配置 28 | 29 | Swoft 应用的 PostgreSQL 配置都在配置文件 app/bean.php 中。 30 | 31 | ```php 32 | use Swoft\Pgsql\PgsqlDb; 33 | 34 | 'pgsql' => [ 35 | 'class' => PgsqlDb::class, 36 | 'host' => '127.0.0.1', 37 | 'port' => 5432, 38 | 'database' => 'dbname', 39 | 'schema' => ['postgis', 'public'], 40 | 'charset' => 'utf8', 41 | 'user' => 'username', 42 | 'password' => 'pass' 43 | ], 44 | ``` 45 | 46 | - class 指定当前配置驱动类型 47 | - host 连接地址 默认 `127.0.0.1` 48 | - port 端口 默认 `5432` 49 | - database 连接数据库 默认 `postgres` 50 | 51 | 52 | ### 连接池配置 53 | 54 | Swoft 所有连接池配置都差不多,配置都在配置文件 app/bean.php 中,默认的连接池名為 pgsql.pool。 55 | 56 | ```php 57 | 'pgsql.pool' => [ 58 | 'class' => \Swoft\Pgsql\Pool::class, 59 | 'pgsqlDb' => bean('pgsql'), 60 | 'minActive' => 2, 61 | 'mixActive' => 20, 62 | 'maxWait' => 0, 63 | 'maxWaitTime' => 0, 64 | 'maxIdleTime' => 60, 65 | ], 66 | ``` 67 | 68 |

69 | 注意:每一个 worker 都会创建一个同样的连接池。并不是越多越好,参数配置要根据,机器配置和 worker 个数衡量。 70 |

71 | 72 | 73 | ### 使用连接池 74 | 75 | 76 | ```php 77 | pgsql->createConnection(); 104 | return $connection->select("SELECT * FROM test;"); 105 | } 106 | 107 | public function fetchNumTest(): array 108 | { 109 | $connection = $this->pgsql->createConnection(); 110 | return $connection->selectFetchNum("SELECT testid, testname FROM test;"); 111 | } 112 | 113 | public function bindingTest(int $id = 1, string name = "myname"): array 114 | { 115 | $connection = $this->pgsql->createConnection(); 116 | return $connection->select("SELECT * FROM test WHERE testid = $1 AND testname = $2;", array($id, $name)); 117 | } 118 | 119 | // 非连接池的原生配置函数 120 | public function copyFromTest(): bool 121 | { 122 | $table = 'test'; 123 | $data = array( 124 | array('1|C252525A|0|0|02921|02921|2|0|Welcome To Swoft|02921||benny|2019-09-03 14:40:55|1|'), 125 | array('2|C252525B|0|0|02921|02921|2|0|Welcome To Swoole|02921||curtis|2019-07-03 14:40:55|1|'), 126 | ); 127 | 128 | $connection = $this->pgsql->createConnection(); 129 | $connection->select("TRUNCATE ".$table.";"); 130 | 131 | return $connection->copyFrom($table, $data); 132 | } 133 | 134 | // 非连接池的原生配置函数 135 | public function copyToTest(): array 136 | { 137 | $table = 'test'; 138 | $connection = $this->pgsql->createConnection(); 139 | return $connection->copyTo($table); 140 | } 141 | } 142 | ``` 143 | 144 | ## 参与贡献 145 | 146 | 欢迎参与贡献,您可以 147 | 148 | - fork 开发仓库 [curtis18/swoft-pgsql](https://github.com/curtis18/swoft-pgsql) 149 | - 修改代码然后发起 PR 150 | - 关于发起PR的[注意事项](https://github.com/swoft-cloud/swoft/issues/829) 151 | -------------------------------------------------------------------------------- /zh-CN/console/input.md: -------------------------------------------------------------------------------- 1 | # 输入对象 2 | 3 | 输入对象是 `Swoft\Console\Input\Input` 的实例,用于获取用户输入的命令参数选项等信息。 4 | 命令逻辑里面,可以通过函数参数和全局函数获取输入输出对象。 5 | 6 | ## 获取输入对象 7 | 8 | ### 通过方法参数 9 | 10 | 如果需要使用输入和输出对象,可以在操作命令函数上,定义输入和输出对象,底层框架会自动注入对象。 11 | 12 | ```php 13 | /** 14 | * Test command 15 | * 16 | * @Command(coroutine=true) 17 | */ 18 | class TestCommand 19 | { 20 | /** 21 | * @param Input $input 22 | * @param Output $output 23 | * 24 | * @CommandMapping("test2") 25 | */ 26 | public function test(Input $input, Output $output) 27 | { 28 | // ...... 29 | } 30 | } 31 | ``` 32 | 33 | ### 使用全局函数 34 | 35 | ```php 36 | /** 37 | * Test command 38 | * 39 | * @Command(coroutine=true) 40 | */ 41 | class TestCommand 42 | { 43 | /** 44 | * @CommandMapping() 45 | */ 46 | public function demo() 47 | { 48 | $input = \input(); 49 | $output = \output(); 50 | // ...... 51 | } 52 | } 53 | ``` 54 | 55 | ## 开始使用 56 | 57 | 通过前一篇 [定义命令](definition.md) 文章,我们已经知道了什么是命令参数,命令选项。 58 | 59 | 现在,终端中执行如下命令,用于演示参数选项等信息的解析: 60 | 61 | ```bash 62 | $ php bin/swoft demo:test status=2 name=john arg0 -s=test --page 23 --id=154 -e dev -v vvv -d -rf --debug --test=false 63 | ``` 64 | 65 | > **注意:** 输入如下的字符串将会认为是布尔值 66 | 67 | - `on|yes|true` -- `true` 68 | - `off|no|false` -- `false` 69 | 70 | ## 获取基本信息 71 | 72 | ```php 73 | echo $input->getScript(); // 'bin/swoft' 执行的入口脚本文件 74 | echo $input->getCommand(); // 'http:start' 命令名称 解析到的第一个参数将会被认为是命令名称,并且不会再存入到 参数列表中 75 | echo $input->getPwd(); // 当前工作目录 76 | ``` 77 | 78 | ## 命令参数信息 79 | 80 | > 通常的参数如 `arg0` 只能根据 index key 来获取值。但是提供以等号(`=`)连接的方式来指定参数名(eg: `status=2`) 81 | 82 | 打印所有的参数信息: 83 | 84 | ```php 85 | var_dump($input->getArgs()); 86 | ``` 87 | 88 | output: 89 | 90 | ```php 91 | array(3) { 92 | 'status' => string(1) "2" 93 | 'name' => string(4) "john" 94 | [0] => string(4) "arg0" 95 | } 96 | ``` 97 | 98 | ### 获取命令参数值 99 | 100 | ```php 101 | // argument 102 | $first = $input->getFirstArg(); // 'arg0' 103 | $status = $input->getArg('status', 'default value'); // '2' 104 | $status = $input->getInt('status'); // 2 105 | // 获取一个必须的参数,若用户没有输入值,将会抛出错误信息 106 | $id = $input->getRequiredArg('id'); 107 | ``` 108 | 109 | ## 命令选项信息 110 | 111 | 获取解析后的选项信息 112 | 113 | - 没有值的选项,将设置默认值为 `bool(true)` 114 | - 短选项不仅仅只是以一个 `-` 开头,而且名称 **只能是一个字符** 115 | - 多个(默认值的)短选项可以合并到一起写。如 `-rf` 会被解析为两个短选项 `'r' => bool(true)` `'f' => bool(true)` 116 | 117 | 打印所有的选项信息: 118 | 119 | ```php 120 | var_dump($input->getOpts()); 121 | // var_dump($input->getLOpts()); // 只打印长选项信息 122 | // var_dump($input->getSOpts()); // 只打印短选项信息 123 | ``` 124 | 125 | output: 126 | 127 | ```php 128 | array(10) { 129 | 's' => string(4) "test" 130 | 'e' => string(3) "dev" 131 | 'v' => string(3) "vvv" 132 | 'd' => bool(true) 133 | 'r' => bool(true) 134 | 'f' => bool(true) 135 | 'page' => string(2) "23" 136 | 'id' => string(3) "154" 137 | 'debug' => bool(true) 138 | 'test' => bool(false) 139 | } 140 | ``` 141 | 142 | ### 获取选项值 143 | 144 | 输入对象中提供了非常多的选项值获取方法,方便快速的获取需要的信息。 145 | 146 | ```php 147 | // option 148 | $page = $input->getOpt('page') // '23' 149 | $page = $input->getIntOpt('page') // 23 150 | $debug = $input->getBoolOpt('debug') // True 151 | $test = $input->getBoolOpt('test') // False 152 | 153 | $d = $input->getBoolOpt('d') // True 154 | // 获取到一个值就返回,对同一个含义的选项选项非常有用 155 | $showHelp = $input->sameOpt(['h','help']); 156 | // 获取一个必须的选项,若用户没有输入值,将会抛出错误信息 157 | $id = $input->getRequiredOpt('id'); 158 | ``` 159 | 160 | ## 读取用户输入 161 | 162 | ```php 163 | echo "Your name:"; 164 | 165 | $name = $input->read(); 166 | echo 'input is ' . $name; // 'inhere' 167 | ``` 168 | 169 | 效果(in terminal): 170 | 171 | ```text 172 | $ Your name: inhere 173 | $ input is inhere 174 | ``` 175 | 176 | 也可以直接将消息文本放入参数 `$name = $input->read("Your name:");` 177 | 178 | -------------------------------------------------------------------------------- /zh-CN/websocket-server/config.md: -------------------------------------------------------------------------------- 1 | # websocket 配置 2 | 3 | websocket 的 host, port 等配置是都是完全可以自定义的。 4 | 配置需要编辑 `app/bean.php` 文件,下面列举了一些简单的配置,你也可以自由组合同时提供多种服务。 5 | 6 | > websocket server 的默认端口是 `18308` 7 | 8 | ## 可配置项 9 | 10 | 可配置项用于 ws server bean 配置,除了 `class` 其他都是 ws server 的属性。 11 | 12 | - `class` 指定 websocket server 的处理类 13 | - `port` 指定 websocket server 的端口 14 | - `listener` 指定其他一同启动的服务,添加端口服务监听,可以多个。 15 | - rpc 启动 `RPC` 服务 16 | - `on` 配置监听的事件 17 | - 注册事件、设置对应事件的处理监听,事件触发组件调用,在任务里面使用 18 | - `setting` 这里是参考 [Swoole Server配置选项](https://wiki.swoole.com/wiki/page/274.html) 19 | - `pidFile` 设置进程 `pid文件` 位置,默认值 `@runtime/swoft.pid` 20 | - `mode` 运行的模式,参考 [Swoole Server 构造函数](https://wiki.swoole.com/wiki/page/14.html) 第三个参数 21 | - `type` 指定Socket的类型,支持TCP、UDP、TCP6、UDP6、UnixSocket Stream/Dgram 等 [Swoole Server 构造函数](https://wiki.swoole.com/wiki/page/14.html) 第四个参数 22 | 23 | ## 基础配置 24 | 25 | ```php 26 | // ... 27 | 'wsServer' => [ 28 | 'class' => WebSocketServer::class, 29 | 'port' => 18307, 30 | 'debug' => env('SWOFT_DEBUG', 0), 31 | /* @see WebSocketServer::$setting */ 32 | 'setting' => [ 33 | 'log_file' => alias('@runtime/swoole.log'), 34 | ], 35 | ], 36 | ``` 37 | 38 | ## 启用http请求处理 39 | 40 | 默认的是没有启用http server功能的。如果你想开启ws时,同时处理http请求。 41 | 42 | ```php 43 | // ... 44 | 'wsServer' => [ 45 | 'class' => WebSocketServer::class, 46 | 'on' => [ 47 | // 加上如下一行,开启处理http请求 48 | SwooleEvent::REQUEST => bean(RequestListener::class), 49 | ], 50 | 'debug' => env('SWOFT_DEBUG', 0), 51 | /* @see WebSocketServer::$setting */ 52 | 'setting' => [ 53 | 'log_file' => alias('@runtime/swoole.log'), 54 | ], 55 | ], 56 | ``` 57 | 58 | ok, 现在 `IP:PORT` 上可以同时处理 http 和 ws 请求了。 59 | 60 | ## 启用wss支持 61 | 62 | 跟在http server启用https类似,在swoft里启用 wss 也非常简单,添加如下的配置即可: 63 | 64 | ```php 65 | 'httpServer' => [ 66 | 'type' => SWOOLE_SOCK_TCP | SWOOLE_SSL, 67 | 68 | /* @see WebSocketServer::$setting */ 69 | 'setting' => [ 70 | 'ssl_cert_file' => '/my/certs/2288803_www.domain.com.pem', 71 | 'ssl_key_file' => '/my/certs/2288803_www.domain.com.key', 72 | ] 73 | ] 74 | ``` 75 | 76 | > 注意: 你必须安装 OpenSSL 库,并且确保安装swoole时是启用了 ssl 选项的。同时,需要设置 `'type' => SWOOLE_SOCK_TCP | SWOOLE_SSL` 77 | 78 | ## 添加RPC服务 79 | 80 | 如果你想开启ws时,同时启动RPC Server服务。 81 | 82 | ```php 83 | // ... 84 | 'wsServer' => [ 85 | 'listener' => [ 86 | 'rpc' => \bean('rpcServer') // 引入 rpcServer 87 | ], 88 | ], 89 | 'rpcServer' => [ 90 | 'class' => ServiceServer::class, 91 | 'port' => 18308, 92 | ], 93 | ``` 94 | 95 | ## 启用全部功能 96 | 97 | - websocket server 98 | - http server 99 | - task process 100 | - rpc server 101 | 102 | ```php 103 | // ... 104 | 'wsServer' => [ 105 | 'class' => WebSocketServer::class, 106 | 'port' => 18307, 107 | 'on' => [ 108 | // 开启处理http请求支持 109 | SwooleEvent::REQUEST => bean(RequestListener::class), 110 | // 启用任务必须添加 task, finish 事件处理 111 | SwooleEvent::TASK => bean(TaskListener::class), 112 | SwooleEvent::FINISH => bean(FinishListener::class) 113 | ], 114 | 'listener' => [ 115 | // 引入 rpcServer 116 | 'rpc' => \bean('rpcServer') 117 | ], 118 | 'debug' => env('SWOFT_DEBUG', 0), 119 | /* @see WebSocketServer::$setting */ 120 | 'setting' => [ 121 | 'log_file' => alias('@runtime/swoole.log'), 122 | // 任务需要配置 task worker 123 | 'task_worker_num' => 2, 124 | 'task_enable_coroutine' => true 125 | ], 126 | ], 127 | 'rpcServer' => [ 128 | 'class' => ServiceServer::class, 129 | 'port' => 18308, 130 | ], 131 | ``` 132 | 133 | ok, 现在通过 `php bin/swoft ws:start` 启动的服务器,就支持上面的全部功能了 134 | 135 | -------------------------------------------------------------------------------- /zh-CN/tcp-server/client-communicate.md: -------------------------------------------------------------------------------- 1 | # 客户端通信 2 | 3 | 你可以直接使用 swoole 提供的 `Swoole\Coroutine\Client` 作为tcp客户端,快速的对接swoft的tcp sever。 4 | 5 | 为了分包和数据解析与tcp server保持一致,你需要依赖tcp协议包: 6 | 7 | ```bash 8 | composer require swoft/tcp 9 | ``` 10 | 11 | 开始之前,首先你得确认你已经启动了tcp server端,并且保持客户端与服务端的 **协议设置是一致的**。 12 | 13 | ## swoft 示例 14 | 15 | ```php 16 | setOpenLengthCheck(true); 35 | 36 | var_dump($proto->getConfig()); 37 | 38 | $host = '127.0.0.1'; 39 | $port = 18309; 40 | 41 | $client = new Client(SWOOLE_SOCK_TCP); 42 | // Notice: config client 43 | $client->set($proto->getConfig()); 44 | 45 | if (!$client->connect((string)$host, (int)$port, 5.0)) { 46 | $code = $client->errCode; 47 | /** @noinspection PhpComposerExtensionStubsInspection */ 48 | $msg = socket_strerror($code); 49 | $output->error("Connect server failed. Error($code): $msg"); 50 | return; 51 | } 52 | 53 | // Send message $msg . $proto->getPackageEOf() 54 | if (false === $client->send($proto->packBody($msg))) { 55 | /** @noinspection PhpComposerExtensionStubsInspection */ 56 | $output->error('Send error - ' . socket_strerror($client->errCode)); 57 | return; 58 | } 59 | 60 | // Recv response 61 | $res = $client->recv(2.0); 62 | if ($res === false) { 63 | /** @noinspection PhpComposerExtensionStubsInspection */ 64 | $output->error('Recv error - ' . socket_strerror($client->errCode)); 65 | return; 66 | } 67 | 68 | if ($res === '') { 69 | $output->info('Server closed connection'); 70 | return; 71 | } 72 | 73 | // unpack response data 74 | [$head, $body] = $proto->unpackData($res); 75 | $output->prettyJSON($head); 76 | $output->writef('server> %s', $body); 77 | } 78 | ``` 79 | 80 | ## 非swoft示例 81 | 82 | > 注意:这里使用的json数据,因此你需要将服务端 `tcpServerProtocol` 的 `type` 配置为 `json` 83 | 84 | ```php 85 | $cmd, 97 | 'data' => $data, 98 | 'ext' => $ext, 99 | ]; 100 | $data = json_encode($req) . PKG_EOF; 101 | fwrite($fp, $data); 102 | 103 | $result = ''; 104 | while (!feof($fp)) { 105 | $tmp = stream_socket_recvfrom($fp, 1024); 106 | 107 | if ($pos = strpos($tmp, PKG_EOF)) { 108 | $result .= substr($tmp, 0, $pos); 109 | break; 110 | } else { 111 | $result .= $tmp; 112 | } 113 | } 114 | 115 | fclose($fp); 116 | return json_decode($result, true); 117 | } 118 | 119 | $ret = request('tcp://127.0.0.1:18309', 'echo', 'i an client'); 120 | 121 | var_dump($ret); 122 | ``` 123 | 124 | ## 测试通信 125 | 126 | 你可以复制上面的示例代码,新建一个php文件来运行测试。 127 | 128 | 当然,最方便直接的就是使用我们 `devtool` 包里提供的 `dclient:tcp` 工具命令。 129 | 130 | 运行:`php bin/swoft dclient:tcp -h` 查看命令帮助 131 | 132 | ![](https://raw.githubusercontent.com/swoft-cloud/swoft-doc/2.x/zh-CN/image/tcp-server/devtool-tcp-test.png) 133 | -------------------------------------------------------------------------------- /zh-CN/websocket-server/module.md: -------------------------------------------------------------------------------- 1 | # websocket 模块 2 | 3 | 在根据上两章安装配置好之后,就可以在 `app/WebSocket` 下创建需要的 websocket 模块来处理相关逻辑 4 | 5 | 在每个模块里允许用户处理的几个事件有 `handshake` `open` `message` `close` 6 | 7 | ## 注解 8 | 9 | ### WsModule 10 | 11 | websocket 模块类注解tag `@WsModule` 12 | 13 | - 注解类: `Swoft\WebSocket\Server\Annotation\Mapping\WsModule` 14 | - 作用范围: `CLASS` 15 | - 拥有属性: 16 | + `path` _string_ 标明了允许ws连接的URI path. 17 | + `controllers` _array_ 绑定到此模块的消息控制器类 18 | + `messageParser` _string_ 绑定到此模块的消息数据解析器 19 | + `defaultOpcode` _integer_ 此模块默认的消息数据 `opcode` 20 | 21 | 示例: 22 | 23 | ```php 24 | /** 25 | * @WsModule("/echo") 26 | */ 27 | ``` 28 | 29 | 上面的注解标明了允许ws连接的URI path. 即客户端请求的ws连接类似: `ws://IP:PORT/echo` 30 | 31 | ### OnHandshake 32 | 33 | 方法注解 `@OnHandshake` 标记处理握手的方法 34 | 35 | - 注解类: `Swoft\WebSocket\Server\Annotation\Mapping\OnHandshake` 36 | - 作用范围: `METHOD` 37 | 38 | > 这方法是可选的。如果没有特殊的需求,可以忽略它,框架会帮你握手并响应握手成功。 39 | 40 | 必须返回含有两个元素的array 41 | 42 | - `bool` 第一个元素的值来决定是否进行握手 43 | - 第二个元素是response对象 - _可以在response设置一些自定义header,body等信息_ 44 | 45 | ### OnOpen 46 | 47 | 在握手成功后,就会触发 open 事件. 方法注解 `@OnOpen` 标记对应方法。 48 | 49 | > 此时开始你就可以给客户端发消息了 :) 50 | 51 | - 注解类: `Swoft\WebSocket\Server\Annotation\Mapping\OnOpen` 52 | - 作用范围: `METHOD` 53 | - 此方法也是可选的,可以没有 54 | 55 | ### OnMessage 56 | 57 | 通过的方法注解 `@OnMessage` 标记一个消息处理方法。 58 | 59 | > 在此阶段你可以接收到客户端的消息和发送消息给对方. 60 | 61 | - 注解类: `Swoft\WebSocket\Server\Annotation\Mapping\OnMessage` 62 | - 作用范围: `METHOD` 63 | - 当你没有绑定消息控制器时,表明你想自己处理消息阶段的逻辑, **此方法是必须存在的**。 64 | - 当你有绑定消息控制器时,框架会自动解析消息并路由到指定的消息处理方法 65 | 66 | ### OnClose 67 | 68 | 通过的方法注解 `@OnClose` 标记一个关闭连接时的处理方法。 69 | 70 | 当客户的关闭连接或者server在其他地方主动关闭连接时,就会触发此事件。 71 | 72 | 你可以在这里做一些连接关闭后的工作, 比如:记录日志,解绑用户等 ... 73 | 74 | - 注解类: `Swoft\WebSocket\Server\Annotation\Mapping\OnClose` 75 | - 作用范围: `METHOD` 76 | - 此方法也是可选的,可以没有 77 | 78 | > 注意:触发此事件时连接已被关闭,不能再给对方发消息 79 | 80 | ## 代码示例 81 | 82 | - 这里面方法上的 server 对象都是 `Swoole\WebSocket\Server` 的实例 83 | 84 | ```php 85 | push($fd, 'hello, welcome! :)'); 127 | } 128 | 129 | /** 130 | * @OnMessage() 131 | * @param Server $server 132 | * @param Frame $frame 133 | */ 134 | public function onMessage(Server $server, Frame $frame) 135 | { 136 | $server->push($frame->fd, 'I have received message: ' . $frame->data); 137 | } 138 | 139 | /** 140 | * On connection closed 141 | * - you can do something. eg. record log 142 | * 143 | * @OnClose() 144 | * @param Server $server 145 | * @param int $fd 146 | */ 147 | public function onClose(Server $server, int $fd): void 148 | { 149 | // you can do something. eg. record log, unbind user... 150 | } 151 | } 152 | ``` 153 | 154 | ## 客户端测试 155 | 156 | 如果你安装并启用了 devtool, 那么你可以打开页面 `IP:PORT/__devtool/ws/test` 来进行ws测试 157 | 158 | - 填上你的ws server地址(注意不要忘了URI path) 159 | - 然后就可以连接上ws server 并收发消息了 160 | - 如果你在前台运行的server 你也能在运行 server的console 上看到ws连接与消息log 161 | 162 | > 当然也可在网上找一个 ws test网页来进行测试 163 | 164 | --------------------------------------------------------------------------------