咚咚技术团队
  • 首页
  • 文章
    • 前端
      • 0.1 + 0.2 精度丢失深究
      • IOS H5 视频无法播放
      • H5 播放 amr 音频文件
      • IOS 10.x 版本在 Taro 中的兼容性问题
      • 百度 UEditor 引发的 cross-iframe 问题解决方案
      • 访问 www.banchengyun.com 时发生了什么
      • decodeURIComponent 与特殊符号
      • 前端埋点
    • 后端
      • Swoole 相关
        • MAC 本地环境执行 GuzzleHttp 时导致 Swoole 进程异常退出
      • Hyperf 相关
        • 在 phpstorm 中调试 hyperf 代码
        • Hyperf 1.x Proxy 缓存失效问题
      • K8s 相关
        • 搭建 k8s 集群
        • 使用 docker-compose 快速搭建 Hyperf + Redis 开发环境
        • Kubernetes Autoscaler
      • 其它
        • 幂等性和原子性
    • 测试
    • 效能提升
      • 优秀开发者的第一步:始于需求分析
      • 优秀开发者的第二步:如何阅读他人的代码
  • 活动
  • 课堂
  • 知识库
    • 公共
      • 什么是流程型组织
      • 半城云集成产品开发流程
      • 阿⾥云 Codeup 代码平台使⽤ & 迁移指南
      • git 使用规范
      • 关于第三方与服务号授权的问题
      • 收不到消息的排查方法
      • 系统安全
      • 前端编码规范
      • 后端编码规范
      • 测试规范
    • 前端
      • 规范
        • 前端编码规范
        • 咚咚技术栈
        • code-review 规范
        • git 工作流
        • Tapd 文档
      • 复盘经验
        • 2021.01 效能、规范、技术债讨论会
      • Code Review
        • SCRM 2020-07
    • 后端
      • 复盘经验
        • SCRM 2020 年 8 月
      • Code Review
        • SCRM 2020-07
    • 测试
      • 复盘经验
        • SCRM 2020 年 8 月
  • 项目文档
    • 前端
    • 后端:小程序
    • 后端:企业微信
  • 接口文档
  • 兴趣小组
    • golang 小组
    • 增长小组
    • 前端小组
  • 书单推荐
  • 生产环境 分析会
    • NO.2022.01
  • 生产环境 可用性
  • 团队活动
    • OpenTalk
      • NO.2021.Q3
      • NO.2020.Q2
    • WalkTogether
  • 关于我们
  • GitBook 使用说明
由 GitBook 提供支持
在本页
  • bcy-next
  • 0x04: 代码基本分层
  • 0x05: 配置
  • 0x06: 接口测试
  • 0x07: 主要的环境变量
  • 0x08: 模型
  • 0x09: 响应
  • 0x10: 异常
  • 0x11: 表/模型关联

这有帮助吗?

  1. 知识库
  2. 公共

后端编码规范

上一页前端编码规范下一页测试规范

最后更新于3年前

这有帮助吗?

bcy-next

0x00: 开发环境需求

  • PHP 7.4

  • Redis

  • Composer

0x01: IDE

  • 使用支持PHP 7.4版本的PHPStorm (2019.3或以上)并安装

  • 设置

0x02: 启动

  • 完整阅读一遍

  • 执行composer install命令安装包

  • 按照需要修改.env文件的环境变量配置

  • 运行composer run start命令启动服务

注意, composer run start使用了Hyperf提供的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: 接口测试

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: 表/模型关联

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: 接口测试

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: 表/模型关联

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;

```

利用Hyperf提供的, 我们可以很方便的创建接口测试用例, 对于一些核心的接口测试覆盖率要求达到100%, 从一个Demo用例开始了解test/Cases/DemoTest.php, 执行composer run test运行测试

表与表之间的关联操作会很频繁, Hyperf提供了非常强大的 能力, 尽量把表与表之间的关联转换为模型与模型间的关联从而避免手动join连表, 在无法避免使用join时所有参与的字段必须加上表名

利用Hyperf提供的, 我们可以很方便的创建接口测试用例, 对于一些核心的接口测试覆盖率要求达到100%, 从一个Demo用例开始了解test/Cases/DemoTest.php, 执行composer run test运行测试

表与表之间的关联操作会很频繁, Hyperf提供了非常强大的 能力, 尽量把表与表之间的关联转换为模型与模型间的关联从而避免手动join连表, 在无法避免使用join时所有参与的字段必须加上表名

Swoole 4.5
PHP Annotation插件
PHPStorm保存自动格式化
Hyperf文档
热更新 Watcher
自动化测试组件
模型关联
自动化测试组件
模型关联