浅谈项目代码规范

浅谈项目代码规范

代码规范,此话题略范。从何说起?各司企皆具规范,大同小异,也有自称一派者,稀也。

官方规范

《PHP PSR 标准规范》是现有的标准规范,本人也是该规范的追随者,求同存异,会加一些自己的习惯进去,此习惯主要来自于项目经验。

如果你还没有看过官方标准规范,建议你进去看下。

代码规范的优势

齐心协力 意思是认识一致,共同努力。出自《墨子·尚贤》。

此成语充分的说明了代码规范的优势。在阅读代码的时候,阅读者会因为代码中的注释,规范命名,加速了非己所写代码的理解。阅读效率上来了,带来了更好的开发效率,行话曰:代码是写给人看的。此处的人,是你也是你的团队成员。

推荐的代码规范

目前我自己形成了一套编写习惯,略觉可扬,此习惯也是今年(2021)初具形态,故以示之,望与众行内人士共论之。若有增进之处,望畅言,笑迎之。

子曰:三人行,必有我师焉。择其善者而从之,其不善者而改之。

代码规范思路举例

我将自己写的 demo 展示如下:

app/Http/Controllers/Api/AuthController.php

    // 会员-登录
    public function login(Request $request)
    {
        $rules = [
            'username' => 'required|alpha_num|between:3,20',
            'password' => 'required|alpha_num|between:6,12',
        ];
        $messages = [];
        // 替换 传参 为指定内容
        $customAttributes = [
            'username' => '用户名',
            'password' => '密码',
        ];
        $validator = Validator::make($request->all(), $rules, $messages, $customAttributes);
        if ($validator->fails())
        {
            return response()->json([
                'code'   => 403,
                'errors' => $validator->errors()->toArray()
            ], 403);
        }

        // users.username
        $username = $request->post('username');
        // users.password
        $password = $request->post('password');

        // 校验用户名是否存在
        if (User::where('username', $username)->exists() === false)
        {
            return response()->json([
                'code'   => 403,
                'errors' => ['username' => '会员不存在']
            ], 403);
        }
        $user = User::where('username', $username)->first();
        // 校验用户密码
        if (User::generatePassword($password, $user->salt) != $user->password)
        {
            return response()->json([
                'code'   => 403,
                'errors' => ['password' => '密码错误']
            ], 403);
        }

        $token  = UserToken::generateToken($user->id);
        $key    = UserToken::getTokenKey($token);
        // 60 * 60 * 24 * 30, 30 day
        $second = 2592000;

        DB::beginTransaction();
        try {
            // redis 写入 token
            Redis::setEx($key, $second, $token);
            // token 写入数据库
            UserToken::setToken($user->id, $token);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            $context = [
                'function'  => __CLASS__.'->'.__FUNCTION__.'()',
                'line'      => __LINE__,
                'error'     => $e->getMessage()
            ];
            Log::channel('api-controller')->error('用户登录', $context);

            return response()->json([
                'code'   => 403,
                'errors' => ['登录失败']
            ], 403);
        }

        $data = [
            'token'     => $token,
            'username'  => $username,
        ];

        return response()->json([
            'code'      => 200,
            'data'      => $data,
            'message'   => 'success'
        ], 200);
    }// login() end

以上代码摘取于 api 控制器中,处理业务为 用户-登录。
代码分为 四个片区:

  1. 输入内容验证区
  2. 业务逻辑区
  3. 业务处理区
  4. 业务处理返回区

每一个片区都有自己的工作内容,互相关联,但互不干扰。如此而来,我们在读代码的时候,就更加容易做区分了。

app/Models/User.php

    /**
     * 生成记录
     *
     * @param string $username          users.username
     * @param string $password          users.password
     * @param string $payment_password  users.payment_password
     * @param string $phone             users.phone
     * @return int
     */
    public static function generateLog(string $username, string $password, string $payment_password = '', string $phone = ''): int
    {
        $salt = self::generateSalt(6);
        if (strlen($payment_password) > 0)
            $payment_password = self::generatePassword($payment_password, $salt);

        $insert = [
            'username'          => $username,
            'password'          => self::generatePassword($password, $salt),
            'payment_password'  => $payment_password,
            'phone'             => $phone,
            'salt'              => $salt,
            'is_lock'           => 'n',
            'is_frozen'         => 'n',
            'created_at'        => date('Y-m-d H:i:s'),
            'updated_at'        => date('Y-m-d H:i:s'),
        ];
        return self::insertGetId($insert);
    }// generateLog() end

模型方法,按照规范,应该是单一功能,如果是多功能,建议做拆分,提升维护性。

结语

寥寥数字,无法完全表明全意。总算是有个开始,将自己的经验总结吐出出来。望多包涵。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 21
fatrbaby

其他都忍了,但是除了类和方法外,大括号另起一行不能忍。

2年前 评论

社区蛮多开源项目都没有这么规范,至少用 php-cs-fixer 可以解决大部分的不规范写法,但是他们没有。

2年前 评论

我想杠 :joy:

变量声明

file
只用一次的数据,不喜欢存储为变量。

表单验证

file

file

我更喜欢用 $this->validate() 来验证,验证错误直接抛出异常。
用异常来返回结果,也就是这段代码在我看来的多余的。

效验存在 & 获取用户

file
如果我直接一个三元,不知道会不会被打…
就像这样

file

事务

file
顺序错误了吧,应该执行 mysql 的,再执行 redis
毕竟 mysql 又回滚不到 redis

感觉这里代码可以再简化的

成功和失败的返回总要封装吧

file

2年前 评论
小李世界 2年前
chowjiawei

检测用户是否存在 验证规则就有啊 为什么要自己写这么多

2年前 评论

我觉得,代码想规范的话,起码业务要分层吧,你这好家伙都在控制器里处理了,丝毫没有封装。现在是用户名密码登录,那么我如果换个手机号,短信验证码登录,下边能复用的业务逻辑再来写一遍么 不合适吧。。。 :joy:

2年前 评论

复杂业务需要注释,简单的一眼就能看懂的也写注释,就显得有点画蛇添足

2年前 评论

file 想要规范的话方法头注释是不是应该声明一下参数和返回值类型啊 :joy:可能这也是我的个人习惯

2年前 评论

这个 if 的括号难受死了,为什么要换行 :joy:

2年前 评论

file

这代码帅不帅 :joy: :joy:

2年前 评论
她来听我的演唱会 2年前
MArtian 2年前
一个人的江湖 (作者) 2年前
一个人的江湖 (作者) 2年前
SunSay 2年前

那四个片区分开写到函数里 做到互不干扰是不是更好。 比如 校验用户名 校验密码 都可以写成独立的方法,这样全都写到控制器里,随着需求变更,控制器会变得越来越大,最后导致无法修改。

在不改变现有逻辑的情况下,向下面这样写,我觉得会更好。

// 会员-登录
    public function login(Request $request)
    {
        $rules = $this->getValidateRule();
        $messages = $this->getValidateMessage();
        // 替换 传参 为指定内容
        $customAttributes = $this->getCustomAttribute();
        $validator = Validator::make($request->all(), $rules, $messages, $customAttributes);
        if ($validator->fails()) {
            return $this->loginResponseWithErrors($validator->errors()->toArray());
        }

        // users.username
        $username = $request->post('username');
        // users.password
        $password = $request->post('password');

        // 校验用户名是否存在
        if (!$this->checkUsernameExists($username)) {
            return $this->loginResponseWithErrors(['username' => '会员不存在']);
        }

        $user = $this->checkUserPassword($username, $password);
        if (false === $user) {
            return $this->loginResponseWithErrors(['password' => '密码错误']);
        }

        $token = $this->generateToken($user);

        if (false === $token) {
            return $this->loginResponseWithErrors(['登录失败']);
        }

        $data = [
            'token'     => $token,
            'username'  => $username,
        ];

        return $this->loginResponseWithSuccess($data);
    }// login() end

    protected function getValidateRule()
    {
        return [
            'username' => 'required|alpha_num|between:3,20',
            'password' => 'required|alpha_num|between:6,12',
        ];
    }

    protected function getValidateMessage()
    {
        return [];
    }

    protected function getCustomAttribute()
    {
        return [
            'username' => '用户名',
            'password' => '密码',
        ];
    }

    protected function loginResponseWithErrors(array $errors)
    {
        return response()->json([
            'code'   => 403,
            'errors' => $errors
        ], 403);
    }

    protected function loginResponseWithSuccess(array $data)
    {
        return response()->json([
            'code'      => 200,
            'data'      => $data,
            'message'   => 'success'
        ], 200);
    }

    protected function checkUsernameExists(string $username) :bool
    {
        return User::where('username', $username)->exists();
    }

    protected function checkUserPassword($username, $password)
    {
        $user = User::where('username', $username)->first();
        // 校验用户密码
        if (User::generatePassword($password, $user->salt) != $user->password)
        {
            return false;
        }
        return $user;
    }

    protected function generateToken($user)
    {
        $token  = UserToken::generateToken($user->id);
        $key    = UserToken::getTokenKey($token);
        // 60 * 60 * 24 * 30, 30 day
        $second = 2592000;

        DB::beginTransaction();
        try {
            // redis 写入 token
            Redis::setEx($key, $second, $token);
            // token 写入数据库
            UserToken::setToken($user->id, $token);

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();

            $context = [
                'function'  => __CLASS__.'->'.__FUNCTION__.'()',
                'line'      => __LINE__,
                'error'     => $e->getMessage()
            ];
            Log::channel('api-controller')->error('用户登录', $context);

            return false;
        }
        return $token;
    }
2年前 评论
fatrbaby

其他都忍了,但是除了类和方法外,大括号另起一行不能忍。

2年前 评论
九霄道长

@fatrbaby 这个问题绝了,但是在phpstrom里默认的格式化就是大括号另起一行。。

2年前 评论
fatrbaby 2年前
小李世界 2年前
WhiteDragon 2年前

谢谢各位同学的谏言,鄙人纳之。古人云:集思广益。三人行,必有我师。君不管是前辈或晚辈,均可互学。晚辈,优于创新,不拘泥于现状,勇于挑战,充满激情等。前辈,优于能纵观全局,取经验而行之。

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!