后端编码规范
bcy-next
0x00: 开发环境需求
PHP 7.4
Redis
Composer
0x01: IDE
使用支持PHP 7.4版本的PHPStorm (2019.3或以上)并安装PHP Annotation插件
0x02: 启动
完整阅读一遍Hyperf文档
执行
composer install
命令安装包按照需要修改
.env
文件的环境变量配置运行
composer run start
命令启动服务
注意, composer run start
使用了Hyperf提供的watcher组件实现了热重启功能修改了代码后无需手动重启, 但是如果有删除文件的动作必须手动重启服务, 详参热更新 Watcher
0x03: 函数编码与PHPDoc
/**
* 这是一个foo函数
* @author 2m
* @param $name string
* @return string
*/
function foo(string $name): string {
return "hello {$name}";
}
上例是一个标准的函数/方法
必须编写PHPDoc, 第一行简述此函数/方法的作用,
@author
为原始作者foo(string $name): string
, 函数的参数列表类型和函数返回类型必须
声明
0x04: 代码基本分层
Controller
控制器层主要负责接收验证客户端的数据, 调用Service
层的代码返回数据给客户端Service
逻辑层是主要逻辑代码的聚集, 接收来自控制器层或者其它Service层的调用, 禁止在Service层中获取请求参数或响应客户端Model
模型层是数据表的实体表示, 以及描述表与表之间的关联关系, 模型层只能做描述
动作, 禁止在模型层编写操作数据库的代码Utils
工具层提供一系列与业务无关联
的助手函数, 反例是编写一个使用$userid获取用户数据的方法, 这属于与业务有关联的方法, 统一放到Service层中
0x05: 配置
在项目中有以下三种配置来源, 各负责不同类型的配置, 如果需要增加配置项需要合理安排
config
目录下的配置文件, 配置一些非敏感和不区分环境的配置, 另外也为各种组件提供配置来源.env
环境变量存放一些区分环境的配置, 比如debug
选项, 在生产环境中是禁用的, 但是在开发环境中开启debug尤其重要ACM
是阿里云的配置中心, 用于存放一些敏感的配置, 比如access_token
之类
0x06: 接口测试
利用Hyperf提供的自动化测试组件, 我们可以很方便的创建接口测试用例, 对于一些核心的接口测试覆盖率要求达到100%
, 从一个Demo用例开始了解test/Cases/DemoTest.php
, 执行composer run test
运行测试
0x07: 主要的环境变量
在.env.example
文件中有详细的可用环境变量列表已经默认的设置, 如果业务中有加入其它的环境变量, 一定要在此文件中写明作用以及默认值
APP_ENV 应用环境, 可用的值为
prod
生产环境,dev
线上测试环境,local
本地开发环境SERVER_WORKER_NUM 服务Worker进程数, 设置为
(null)
即自动根据CPU核心数设置, 在本地开发环境中设置为1
可以减少资源消耗
0x08: 模型
可以通过Hyperf提供的gen:model
命令快速生成模型类文件, 模型类文件生成后要检查$table
属性是否和数据库表名一致, 注意模型类以下默认设置
class Foo extends \Hyperf\Database\Model\Model {
protected $table = 'foo';
protected $dateFormat = 'U';
const CREATED_AT = 'create_time';
const UPDATED_AT = 'update_time';
}
这意味着每个数据表都要求有create_time
和update_time
字段并且字段类型为int
0x09: 响应
多数情况下使用AbstractController::ok
和AbstractController::error
方法可以完成响应动作, 但是如果需要修改响应头或者设置其它的HTTP状态码, 参考下例代码
use App\Core\Response;
Response::header('x-foo', 'bar'); // 设置x-foo响应头
Response::statusCode(201); // 设置响应状态码为201
// 使用上例设置了响应后, 必须用过`ok`或`error`方法或下面两个方法结束请求才会生效
Response::send('Other Content'); // 发送自定义内容
Response::asJson(['foo' => 'bar']); // 发送自定义JSON内容
0x10: 异常
当前代码环境无法处理的异常一定要抛出到上一层让调用者知道发生了什么, 下例是反例
class Service
{
public function getUser() {
try {
// some code...
// throw exception!
}catch(Throwable$exception){
return false;
}
}
}
class Controller {
public function index(Service $service) {
if(!$service->getUser()) {
// some code...
}
}
}
上例getUser
方法调用了其它的代码并发生了异常, 但是并没有处理这个异常而是直接返回了false
, 这会增加debug成本, 如果这个异常在getUser
函数内无法恢复, 就必须抛出到上一层让控制器知道发生了什么
class Service
{
public function getUser() {
try {
// some code...
// throw exception!
}catch(Throwable $exception){
throw $exception;
}
// some code...
}
}
class Controller extends \App\Controller\AbstractController {
public function index(Service $service) {
list($data, $exception) = \App\Utils\Functions::pcall(fn() => $service->getUser());
if(!$exception) {
$this->error($exception);
} else{
$this->ok($data);
}
}
public function better(Service $service) {
$this->pcall(fn() => $service->getUser());
}
}
上例使用了PHP 7.4的新特性箭头函数, 配合pcall
助手方法可以把try{}catch
代码块转换成golang
风格的异常处理逻辑, better
方法中提供了一种更方便的处理方法, 它与index
的处理逻辑等效
0x11: 表/模型关联
表与表之间的关联操作会很频繁, Hyperf提供了非常强大的模型关联 能力, 尽量把表与表之间的关联转换为模型与模型间的关联从而避免手动join
连表, 在无法避免使用join
时所有参与的字段必须
加上表名
use App\Model\Model;
class Order extends Model {
public function user() {
// 声明Order -> User模型间的关联关系
return $this->belongsTo(User::class, 'user_id', 'user_id');
}
}
class User extends Model {}
// Bad: 使用join连接表
Order::query()
->join('user', 'user.user_id', '=', 'order.user_id')
->where(...)
->select(['user.user_id', 'order.order_id'])->get();
// Better: 使用关联模型
Order::query()->where(...)->user;## bcy-next
#### 0x00: 开发环境需求
* PHP 7.4
* [Swoole 4.5](https://www.swoole.com/)
* Redis
* Composer
#### 0x01: IDE
* 使用支持PHP 7.4版本的PHPStorm (2019.3或以上)并安装[PHP Annotation插件](https://plugins.jetbrains.com/plugin/7320-php-annotations)
* 设置[PHPStorm保存自动格式化](https://blog.csdn.net/jefry52/article/details/103149207)
#### 0x02: 启动
* 完整阅读一遍[Hyperf文档](https://hyperf.wiki/2.0/)
* 执行`composer install`命令安装包
* 按照需要修改`.env`文件的环境变量配置
* 运行`composer run start`命令启动服务
注意, `composer run start`使用了Hyperf提供的watcher组件实现了热重启功能修改了代码后无需手动重启, 但是如果有删除文件的动作必须手动重启服务, 详参[热更新 Watcher](https://hyperf.wiki/2.0/#/zh-cn/watcher)
#### 0x03: 函数编码与PHPDoc
```php
/**
* 这是一个foo函数
* @author 2m
* @param $name string
* @return string
*/
function foo(string $name): string {
return "hello {$name}";
}
上例是一个标准的函数/方法
必须编写PHPDoc, 第一行简述此函数/方法的作用,
@author
为原始作者foo(string $name): string
, 函数的参数列表类型和函数返回类型必须
声明
0x04: 代码基本分层
Controller
控制器层主要负责接收验证客户端的数据, 调用Service
层的代码返回数据给客户端Service
逻辑层是主要逻辑代码的聚集, 接收来自控制器层或者其它Service层的调用, 禁止在Service层中获取请求参数或响应客户端Model
模型层是数据表的实体表示, 以及描述表与表之间的关联关系, 模型层只能做描述
动作, 禁止在模型层编写操作数据库的代码Utils
工具层提供一系列与业务无关联
的助手函数, 反例是编写一个使用$userid获取用户数据的方法, 这属于与业务有关联的方法, 统一放到Service层中
0x05: 配置
在项目中有以下三种配置来源, 各负责不同类型的配置, 如果需要增加配置项需要合理安排
config
目录下的配置文件, 配置一些非敏感和不区分环境的配置, 另外也为各种组件提供配置来源.env
环境变量存放一些区分环境的配置, 比如debug
选项, 在生产环境中是禁用的, 但是在开发环境中开启debug尤其重要ACM
是阿里云的配置中心, 用于存放一些敏感的配置, 比如access_token
之类
0x06: 接口测试
利用Hyperf提供的自动化测试组件, 我们可以很方便的创建接口测试用例, 对于一些核心的接口测试覆盖率要求达到100%
, 从一个Demo用例开始了解test/Cases/DemoTest.php
, 执行composer run test
运行测试
0x07: 主要的环境变量
在.env.example
文件中有详细的可用环境变量列表已经默认的设置, 如果业务中有加入其它的环境变量, 一定要在此文件中写明作用以及默认值
APP_ENV 应用环境, 可用的值为
prod
生产环境,dev
线上测试环境,local
本地开发环境SERVER_WORKER_NUM 服务Worker进程数, 设置为
(null)
即自动根据CPU核心数设置, 在本地开发环境中设置为1
可以减少资源消耗
0x08: 模型
可以通过Hyperf提供的gen:model
命令快速生成模型类文件, 模型类文件生成后要检查$table
属性是否和数据库表名一致, 注意模型类以下默认设置
class Foo extends \Hyperf\Database\Model\Model {
protected $table = 'foo';
protected $dateFormat = 'U';
const CREATED_AT = 'create_time';
const UPDATED_AT = 'update_time';
}
这意味着每个数据表都要求有create_time
和update_time
字段并且字段类型为int
0x09: 响应
多数情况下使用AbstractController::ok
和AbstractController::error
方法可以完成响应动作, 但是如果需要修改响应头或者设置其它的HTTP状态码, 参考下例代码
use App\Core\Response;
Response::header('x-foo', 'bar'); // 设置x-foo响应头
Response::statusCode(201); // 设置响应状态码为201
// 使用上例设置了响应后, 必须用过`ok`或`error`方法或下面两个方法结束请求才会生效
Response::send('Other Content'); // 发送自定义内容
Response::asJson(['foo' => 'bar']); // 发送自定义JSON内容
0x10: 异常
当前代码环境无法处理的异常一定要抛出到上一层让调用者知道发生了什么, 下例是反例
class Service
{
public function getUser() {
try {
// some code...
// throw exception!
}catch(Throwable$exception){
return false;
}
}
}
class Controller {
public function index(Service $service) {
if(!$service->getUser()) {
// some code...
}
}
}
上例getUser
方法调用了其它的代码并发生了异常, 但是并没有处理这个异常而是直接返回了false
, 这会增加debug成本, 如果这个异常在getUser
函数内无法恢复, 就必须抛出到上一层让控制器知道发生了什么
class Service
{
public function getUser() {
try {
// some code...
// throw exception!
}catch(Throwable $exception){
throw $exception;
}
// some code...
}
}
class Controller extends \App\Controller\AbstractController {
public function index(Service $service) {
list($data, $exception) = \App\Utils\Functions::pcall(fn() => $service->getUser());
if(!$exception) {
$this->error($exception);
} else{
$this->ok($data);
}
}
public function better(Service $service) {
$this->pcall(fn() => $service->getUser());
}
}
上例使用了PHP 7.4的新特性箭头函数, 配合pcall
助手方法可以把try{}catch
代码块转换成golang
风格的异常处理逻辑, better
方法中提供了一种更方便的处理方法, 它与index
的处理逻辑等效
0x11: 表/模型关联
表与表之间的关联操作会很频繁, Hyperf提供了非常强大的模型关联 能力, 尽量把表与表之间的关联转换为模型与模型间的关联从而避免手动join
连表, 在无法避免使用join
时所有参与的字段必须
加上表名
use App\Model\Model;
class Order extends Model {
public function user() {
// 声明Order -> User模型间的关联关系
return $this->belongsTo(User::class, 'user_id', 'user_id');
}
}
class User extends Model {}
// Bad: 使用join连接表
Order::query()
->join('user', 'user.user_id', '=', 'order.user_id')
->where(...)
->select(['user.user_id', 'order.order_id'])->get();
// Better: 使用关联模型
Order::query()->where(...)->user;
```
最后更新于
这有帮助吗?