使用 git-hooks + phpcs + composer-scripts 践行PHP编码规范
Published on Jul 22, 2019
背景
关于代码规范的重要性,诸如促进团队合作、降低维护成本、有助于代码审查、有助于自身成长等,这里就不详细赘述了,直接进入正题。
PHP编码规范
当前主流的PHP编码规范是由 PHP FIG 组织制定的 PSR 系列规范,如 PSR-1
, PSR-2
, PSR-4
。
当然还是有其他一些规范的存在,如 PEAR规范 和 Symfony规范。我们使用的主要是 PSR-2
规范。
用于检查和修复代码规范的工具主要有 PHP_CodeSniffer 和 PHP-CS-Fixer。
如果你还想了解这两个工具的差异,可以看下这个 ISSUE。我们本次使用的是 PHP_CodeSniffer
。
Git钩子
和其它版本控制系统一样,Git 能在特定的重要动作发生时触发自定义脚本。
有两组这样的钩子:客户端的和服务器端的。
客户端钩子由诸如提交和合并这样的操作所调用,而服务器端钩子作用于诸如接收被推送的提交这样的联网操作。
钩子都被存储在 Git 目录下的 hooks 子目录中。 也即绝大部分项目中的 .git/hooks
。
查看一下你项目中的该目录, 就会看到诸如 pre-commit.sample
, pre-push.sample
, pre-receive.sample
等文件。
要使用他们,只需要把 .sample
后缀去掉,然后编辑自己的代码即可。
这里我们用的是 pre-commit
钩子,即在键入提交信息 git commit
前运行。
它一般用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。
如果该钩子以非零值退出,Git 将放弃此次提交,不过也可以用 git commit --no-verify
来绕过这个环节。
Show me the code
废话不多说,直接上代码吧。 以下为 .git/hooks/pre-commit
的代码:
#!/bin/sh PROJECT=`php -r "echo dirname(dirname(dirname(realpath('$0'))));"` STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php` # Determine if a file list is passed if [ "$#" -eq 1 ] then oIFS=$IFS IFS=' ' SFILES="$1" IFS=$oIFS fi SFILES=${SFILES:-$STAGED_FILES_CMD} echo "Checking PHP Lint..." for FILE in $SFILES do php -l -d display_errors=0 $PROJECT/$FILE if [ $? != 0 ] then echo "Fix the error before commit." exit 1 fi FILES="$FILES $PROJECT/$FILE" done if [ "$FILES" != "" ] then echo "Running Code Sniffer..." ./vendor/bin/phpcs $FILES if [ $? != 0 ] then echo "Fix the error before commit!" echo "Run" echo " ./vendor/bin/phpcbf $FILES" echo "for automatic fix or fix it manually." exit 1 fi fi exit $?
通过代码也能看出,我们直接使用了当前项目中的 ./vendor/bin/phpcs
, 即需要我们在项目中加载 PHP_CodeSniffer
包。
执行 composer require squizlabs/php_codesniffer --dev
即可。
另外,在hook中我们也没有具体指明phpcs要使用哪种规范标准,
因为这更多是取决于我们自己的项目,具体的规则定义在项目根目录下的 phpcs.xml
配置文件中即可。
如果你想参考的话,这里 是一个我们使用的关于 yii2
的规则。
安装的Git钩子无法加入版本库, 每个成员,每个环境都要重复装一遍?
.git
目录下的文件无法加入版本库,这就导致了团队成员中的每个人都要手动安装一次钩子,或者你的多套环境也要重复安装钩子,这当然是很不方便的。
为了解决这个问题,我们可以利用 composer
的钩子来自动完成安装git钩子的过程。
Composer 在执行的过程中会触发一些事件,我们可以通过监控这些事件来自动触发一些脚本的执行。
就自动安装git钩子这个需求,我们可以使用 post-install-cmd
钩子,即当项目里有 composer.lock
文件的情况下调用 install 命令后执行安装脚本。
安装脚本就用最简单的单行PHP脚本即可。
同样,直接上代码:
- 将git的pre-commit钩子脚本放在项目根目录下,命名为
.git-pre-commit
, 并加入版本库; 编辑项目中的
composer.json
文件:{ "scripts": { "post-install-cmd": [ "php -r \"is_dir('.git/hooks') && !is_file('.git/hooks/pre-commit') && copy('.git-pre-commit', '.git/hooks/pre-commit') && chmod('.git/hooks/pre-commit', 0755);\"" ] } }
加入如上代码后,当执行
composer install
后,会自动执行那行php代码, 即简单的将.git-pre-commit
钩子脚本复制到.git/hooks/pre-commit
。当然了,借助
composer
的脚本特性,你也可以继续定义其他的一些常用命令,比如:执行编码规范检查、执行编码规范修复等。 代码简单如下:{ "scripts": { "cs": "phpcs .", "cbf": "phpcbf ." } }
定义好之后,在项目根目录下执行
composer cs
即可对全项目进行编码规范检查,执行composer cbf
即可对全项目进行编码规范修复。
结束语
我们日常用到的一些工具类确实都很强大,只是平时我们都只满足于最简单的日常使用。也许多看一眼文档,就能学习一些更高阶的用法。 希望自己能对日常使用的一些工具能较深入的了解学习一下,也许学习会花一些时间,但是更能为以后的开发节约不少时间,避免重复劳动。