11. Laravel基于php_codesniffer代码风格检测

翟嘉年
2023-12-01
  1. 安装插件

    composer require squizlabs/php_codesniffer --dev
    composer require brainmaestro/composer-git-hooks:* --dev
    
  2. 运行测试

    root@d92a8d4c6792:/var/www/ogenes/Genes-Admin# ./vendor/bin/phpcs --standard=PSR1,PSR2 app/ routes/ config/
    
  3. 自定义校验规则

    #vim _php_codesniffer.xml
    <?xml version="1.0"?>
    <ruleset name="custom">
        <description>The custom coding standard.</description>
    
    
        <!--
        See the [documented coding standard](http://symfony.com/doc/current/contributing/code/standards.html)
    
        This CodeSniffer standard does not yet enforce the following:
    
        # Structure
    
        * Declare class properties before methods;
        * Declare public methods first, then protected ones and finally private ones.
        * Use namespaces for all classes;
        * Add PHPDoc blocks for all classes, methods, and functions;
        * The @package and @subpackage annotations are not used.
        * Use uppercase strings for constants with words separated with underscores
    
        # Naming Conventions
    
        * Use underscores for option, argument, parameter names;
        -->
        <!-- 文件 -->
        <!-- 所有 PHP 文件都必须使用 UTF-8 编码,文件头不允许出现 BOM 标记。 -->
        <rule ref="Generic.Files.ByteOrderMark"/>
        <!-- 所有 PHP 文件都必须使用 Unix LF 格式作为行的结尾。 -->
        <rule ref="Generic.Files.LineEndings">
            <properties>
                <property name="eolChar" value="\n"/>
            </properties>
        </rule>
    
        <!-- 在只包含有 PHP 代码的文件中不允许使用 ?> 作为结束标签。 -->
        <rule ref="Zend.Files.ClosingTag"/>
        <!-- 不允许使用short tag"<?"开头,必须使用"<?php"开头. -->
        <rule ref="Generic.PHP.DisallowShortOpenTag"/>
    
        <!-- 缩进 -->
        <!-- 代码缩进统一为 4 个空格,不允许使用 tab。 -->
        <rule ref="Generic.WhiteSpace.ScopeIndent">
            <exclude name="Generic.WhiteSpace.ScopeIndent.Incorrect" />
            <exclude name="Generic.WhiteSpace.ScopeIndent.IncorrectExact" />
        </rule>
        <rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
    
        <!-- 行 -->
        <!-- 在非空行的结尾不允许出现尾随空白 -->
        <rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
        <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
            <properties>
                <property name="ignoreBlankLines" value="true"/>
            </properties>
            <exclude-pattern>*/app/Facades/*</exclude-pattern>
            <exclude-pattern>*/app/Exceptions/*</exclude-pattern>
            <exclude-pattern>*/app/Console/Kernel.php</exclude-pattern>
            <exclude-pattern>*/bootstrap/*</exclude-pattern>
        </rule>
        <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile">
            <severity>0</severity>
        </rule>
        <!-- 每一行代码只允许一条语句。 -->
        <rule ref="Generic.Formatting.DisallowMultipleStatements"/>
        <!-- 方法与方法之间,都至少有一个空行。 -->
        <rule ref="Squiz.WhiteSpace.FunctionSpacing">
            <properties>
                <property name="spacing" value="1"/>
            </properties>
            <exclude name="Squiz.WhiteSpace.FunctionSpacing.AfterLast" />
            <exclude name="Squiz.WhiteSpace.FunctionSpacing.BeforeFirst" />
        </rule>
    
        <!-- PHP 关键字 和 true/false/null -->
        <!-- PHP关键字必须使用小写。 -->
        <rule ref="Generic.PHP.LowerCaseKeyword" />
        <!-- 关键字 true, false 和 null 必须使用小写。 -->
        <rule ref="Generic.PHP.LowerCaseConstant"/>
        <!-- =、=> 等操作符的前后都必须有一个空格 -->
        <rule ref="Squiz.WhiteSpace.OperatorSpacing">
            <exclude name="Squiz.WhiteSpace.OperatorSpacing.SpacingAfter" />
        </rule>
    
        <!-- 字符串 -->
        <!-- 不允许对两个不带变量的字符串使用连接符,应该用一个字符串表示. -->
        <rule ref="Generic.Strings.UnnecessaryStringConcat"/>
    
        <!-- 注释 -->
        <!-- 每个函数/方法必须有文字描述 -->
        <rule ref="PEAR.Commenting.FunctionComment">
            <exclude-pattern>*/app/Console/Kernel.php</exclude-pattern>
            <exclude-pattern>*/app/Exceptions/*</exclude-pattern>
            <exclude-pattern>*/app/Facades/*</exclude-pattern>
            <exclude name="PEAR.Commenting.FunctionComment.MissingReturn" />
            <exclude name="PEAR.Commenting.FunctionComment.MissingParamComment" />
            <exclude name="PEAR.Commenting.FunctionComment.SpacingAfterParamType" />
            <exclude name="PEAR.Commenting.FunctionComment.SpacingAfterParamName" />
            <exclude name="PEAR.Commenting.FunctionComment.ExtraParamComment" />
        </rule>
    
    
        <!-- 命名空间(namespace)和 use 关键字的使用 -->
        <!-- 在 use 定义块之后必须有一个空行 -->
        <rule ref="PSR2.Namespaces.UseDeclaration">
            <exclude-pattern>*/app/Models/*</exclude-pattern>
        </rule>
    
        <!-- 类、类属性和类方法 -->
        <!-- 类、接口和特征的名称必须以大写开头,如:PartnerInfo、Partner_Info -->
        <rule ref="Squiz.Classes.ValidClassName"/>
        <rule ref="Squiz.Functions.LowercaseFunctionKeywords"/>
        <!-- 一个文件中只能包含类、接口和特征中的一个,不允许出现多个. -->
        <rule ref="Generic.Files.OneClassPerFile"/>
        <!-- 一个文件中只能包含类、接口和特征中的一个,不允许出现多个. -->
        <rule ref="Generic.Files.OneInterfacePerFile"/>
    
        <!-- 类常量 -->
        <!-- 类的常量定义必须全部为大写,并用下划线分割每个单词,如:BRAND_DRAFT、BRAND_DELETED -->
        <rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
    
    
        <!-- 函数定义 -->
        <rule ref="Squiz.Functions.FunctionDeclaration"/>
        <rule ref="Squiz.NamingConventions.ValidFunctionName">
            <exclude name="Squiz.NamingConventions.ValidFunctionName.PrivateNoUnderscore" />
        </rule>
    
        <!-- 函数参数 -->
        <!--
            在参数队列中,逗号之前不允许出现空格,逗号之后必须有一个空格。
            在方法的参数队列中定义带默认值的参数时,必须放到队列的末尾。
            参数队列可以被写到多行,每一行都需要用一个同样的缩进,
            第一个参数必须被写到新的一行,并且每一行只能有一个参数。
            当参数队列被写到多行时,结束括号和开始大括号必须被写到一行,两者之间必须有一个空格。
        -->
        <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
            <properties>
                <property name="equalsSpacing" value="1"/>
            </properties>
        </rule>
        <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterHint">
            <severity>0</severity>
        </rule>
    
    
        <!-- 类方法和一般函数(function)的调用 -->
        <!--
            当调用一个类方法或者一般函数时,方法或函数名称和开始括号之间不允许出现空格,
            在开始括号之后和结束括号之前都不允许出现空格。
            在参数队列里,逗号之前不允许出现空格,逗号之后必须有一个空格。
            参数队列可以被写到多行,每一行都需要用一个同样的缩进,
            第一个参数必须被写到新的一行,并且每一行只能有一个参数。
        -->
        <rule ref="PEAR.WhiteSpace.ObjectOperatorIndent"/>
        <rule ref="PEAR.Functions.FunctionCallSignature">
            <exclude name="PEAR.Functions.FunctionCallSignature.Indent"/>
            <exclude name="PEAR.Functions.FunctionCallSignature.CloseBracketLine"/>
            <exclude name="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket"/>
        </rule>
    
        <!-- 控制结构 -->
        <!--
            控制结构的一般样式规则如下:
            在控制结构的关键字后面必须有一个空格
            开始括号之后不能出现空格
            结束括号之前不能出现空格
            在结束括号和开始大括号之间必须有一个空格
            代码块(结构体)必须有一个缩进
            结束大括号必须在代码块结束后的下一行
            每个代码块必须被封装到一个大括号内,以标准化代码结构,同时减少在添加代码时间引入错误的可能性。
        -->
        <rule ref="Squiz.ControlStructures.ControlSignature"/>
        <!-- 必须用 elseif 代替 else if,让控制关键字看上去是一个单词,同时避免出现代码块逻辑错误。 -->
        <rule ref="PSR2.ControlStructures.ElseIfDeclaration"/>
    </ruleset>
    
    
    # -s 参数会显示生效的规则,便于调试。
    root@d92a8d4c6792:/var/www/ogenes/Genes-Admin# ./vendor/bin/phpcs --standard=./_php_codesniffer.xml  -s app/ 
    
    FILE: /var/www/ogenes/Genes-Admin/app/Providers/MD5HashServiceProvider.php
    ----------------------------------------------------------------------------------------------------
    FOUND 1 ERROR AFFECTING 1 LINE
    ----------------------------------------------------------------------------------------------------
     32 | ERROR | Missing doc comment for function provides()
        |       | (PEAR.Commenting.FunctionComment.Missing)
    ----------------------------------------------------------------------------------------------------
    
    Time: 207ms; Memory: 10MB
    
    
  4. 基于git修改的文件检测,同时支持docker

    #vim php_codesniffer
    #!/usr/bin/env bash
    
    appPath=ogenes/Genes-Admin
    phpversion=php81
    hasError=false
    
    [ -z ${DOCKER_LNMP+x} ]&&DOCKER_LNMP=N
    
    CHANGE_FILES=$(git diff --name-only --diff-filter=ACM | grep -E '\.(php|phtml)$')
    for cf in ${CHANGE_FILES[@]}
    do
        if [ $DOCKER_LNMP = "Y" ]; then
            ret=`docker exec $phpversion bash -c \
            "cd $appPath; ./vendor/bin/$1 --standard=./_php_codesniffer.xml $cf"`
        else
            ret=`$phpversion ./vendor/bin/$1 --standard=./_php_codesniffer.xml $cf`
        fi
    
        if [ -n "$ret" ];then
            echo "${ret}"
        fi
    
        tmpError=`echo $ret| grep -E 'FOUND [1-9]+ ERROR'`
        if [ -n "$tmpError" ]; then
            hasError=true
        fi
    
    done
    
    if $hasError
    then
        exit 1
    else
    		echo 'Success!'
        exit 0
    fi
    
    
  5. 加入composer 命令

    修改 composer.json
    "scripts": {
        "post-autoload-dump": [
          "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
          "@php artisan package:discover --ansi",
          "php81 ./vendor/bin/cghooks update",
          "php81 ./vendor/bin/phpcs --config-set php_version 80108",
          "php81 ./vendor/bin/phpcs --config-set colors 1",
          "php81 ./vendor/bin/phpcs --config-set encoding utf-8"
        ],
    	………………
      ………………
        "lint": "./php_codesniffer phpcs",
        "lint-fix": "./php_codesniffer phpcbf"
      },
    
    composer update
    
  6. 测试

    #随便修改 AuthController.php 一行代码
    
    root@d92a8d4c6792:/var/www/ogenes/Genes-Admin# composer run lint
    > ./php_codesniffer phpcs
    
    FILE: /var/www/ogenes/Genes-Admin/app/Http/Controllers/User/AuthController.php
    ------------------------------------------------------------------------------
    FOUND 4 ERRORS AFFECTING 4 LINES
    ------------------------------------------------------------------------------
     12 | ERROR | [ ] Missing doc comment for function login()
     34 | ERROR | [x] Expected 1 blank line after function; 0 found
     35 | ERROR | [ ] Missing doc comment for function logout()
     44 | ERROR | [ ] Missing doc comment for function userinfo()
    ------------------------------------------------------------------------------
    PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
    ------------------------------------------------------------------------------
    
    Time: 122ms; Memory: 10MB
    Script ./php_codesniffer phpcs handling the lint event returned with error code 1
    root@d92a8d4c6792:/var/www/ogenes/Genes-Admin# 
    
  7. 加上钩子,pre-commit强制检测

      "extra": {
        "laravel": {
          "dont-discover": []
        },
        "hooks": {
          "pre-commit": [
            "echo committing as $(git config user.name).",
            "composer run lint"
          ]
        }
      },
    
    composer update
    
  8. 提交测试

    #docker环境中,在宿主机
    ➜  Genes-Admin git:(ogenes) ✗ export DOCKER_LNMP=Y
    ➜  Genes-Admin git:(ogenes) ✗ git add .                   
    ➜  Genes-Admin git:(ogenes) ✗ git commit -m '代码风格检测'
    committing as 易怀源.
    > ./php_codesniffer phpcs
    
    FILE: .../ogenes/Genes-Admin/app/Http/Controllers/User/AuthController.php
    ----------------------------------------------------------------------
    FOUND 4 ERRORS AFFECTING 4 LINES
    ----------------------------------------------------------------------
     12 | ERROR | [ ] Missing doc comment for function login()
     34 | ERROR | [x] Expected 1 blank line after function; 0 found
     35 | ERROR | [ ] Missing doc comment for function logout()
     44 | ERROR | [ ] Missing doc comment for function userinfo()
    ----------------------------------------------------------------------
    PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
    ----------------------------------------------------------------------
    
    Time: 133ms; Memory: 10MB
    Script ./php_codesniffer phpcs handling the lint event returned with error code 1
    
    
    
    #自动修正
    ➜  Genes-Admin git:(ogenes) ✗ composer run lint-fix
    > ./php_codesniffer phpcbf
    
    PHPCBF RESULT SUMMARY
    ----------------------------------------------------------------------
    FILE                                                  FIXED  REMAINING
    ----------------------------------------------------------------------
    ...dmin/app/Http/Controllers/User/AuthController.php  1      3
    ----------------------------------------------------------------------
    A TOTAL OF 1 ERROR WERE FIXED IN 1 FILE
    ----------------------------------------------------------------------
    
    Time: 192ms; Memory: 10MB
    Success!
    
    #无法自动修正的手动修正
    
    #然后测试
    ➜  Genes-Admin git:(ogenes) ✗ git add .            
    ➜  Genes-Admin git:(ogenes) ✗ git commit -m '代码风格检测'
    committing as 易怀源.
    > ./php_codesniffer phpcs
    Success!
    [ogenes 1941357] 代码风格检测
     5 files changed, 458 insertions(+), 74 deletions(-)
     create mode 100644 _php_codesniffer.xml
     create mode 100755 php_codesniffer
     
    
  9. 修正历史提交的文件

    #自动修正
    root@d92a8d4c6792:/var/www/ogenes/Genes-Admin# ./vendor/bin/phpcbf --standard=./_php_codesniffer.xml app config routes
    
    #手动修正
    root@d92a8d4c6792:/var/www/ogenes/Genes-Admin# ./vendor/bin/phpcs --standard=./_php_codesniffer.xml -s app config routes
    
    
 类似资料: