推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

coding01發表於2019-03-04

我相信每個公司都有一套完備的程式碼規範標準,但標準是標準,如何能有效的讓所有人遵守,那就要工具的輔助和實時提醒了。

如前端 vue 的大家基本都會使用 eslint 來約束我們的程式碼,一旦多一個空格都會提示你有問題,當 npm run dev 或者 npm run watch 就會提示你哪哪哪不符合規範。

在 Laravel 開發中,照樣也有類似的工具,這也是本文的所要推薦的:phpcs

在開始使用 phpcs 之前,我們簡單來說說 Laravel 的程式碼規範標準

Laravel 程式碼規範

Laravel follows the PSR-2 coding standard and the PSR-4 autoloading standard.

來自 Laravel 的說明:laravel.com/

幾個程式碼規範的含義

  1. PSR-0 自動載入規範 (已棄用)
  2. PSR-1 基礎編碼規範
  3. PSR-2 編碼風格規範
  4. PSR-3 日誌介面規範
  5. PSR-4 自動載入規範
  6. PSR-5 PHPDoc 標準 (未通過)
  7. PSR-6 快取介面規範
  8. PSR-7 HTTP 訊息介面規範
  9. PSR-8 Huggable 介面 (未通過)
  10. PSR-9 專案安全問題公示 (未通過)
  11. PSR-10 專案安全上報方法 (未通過)
  12. PSR-11 容器介面
  13. PSR-12 編碼規範擴充
  14. PSR-13 超媒體連結
  15. PSR-14 事件分發器 (未通過)
  16. PSR-15 HTTP 請求處理器
  17. PSR-16 快取介面

其實現在很多網站已經掛出 PSR-2 編碼規範的說明了,推薦看下面這個:

laravel-china.org/docs/psr/ps…

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

但我在實際使用時,除了能夠按照上面說的規範來,還有一塊重要的內容他們沒提。

檔案和類註釋

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

主要包含以上內容塊:檔案說明、PHP 版本號、還有就是按順序的這五要素:(category, package, author, license, link),而且這五要素排版要對齊哦,一般人我不告訴哦~~~

方法註釋

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

主要包含:方法說明、空一行、引數塊 (型別、引數名、含義 —— 這個需要對齊)、空一行、最後 return 型別。

安裝 phpcs

使用 phpcs 之前,還是需要先知道這個東西是什麼吧?

PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.

摘自:github.com/squizlabs/P…

主要包含兩個工具:phpcs 和 phpcbf (這個之後再說)。phpcs 主要對 PHPJavaScriptCSS 檔案定義了一系列程式碼規範標準,如我們會使用的,也是 Laravel 使用的 PHP PSR-2標準,能夠檢測出不符合我們定義的程式碼規範的程式碼,併發出警告和錯誤,當然我們也可以設定報錯的級別。

對於 phpcs 的使用,主要有兩個途徑:

  1. 在本地開發過程中,實時對我們的程式碼進行檢測,讓程式碼提交版本庫時,就已經符合規範標準了;
  2. 在伺服器對提交的程式碼進行檢測,如果不符合標準的,則不入程式碼庫,打回要求修改。

下面我們開始說說根據不同方法,如何安裝 phpcs 工具的。

composer

composer global require "squizlabs/php_codesniffer"
複製程式碼

寫 Laravel 程式碼的同學,對使用 composer 應該很熟悉了,這種方法比較推崇。但主要區分為是「全域性安裝」還是按「專案安裝」。

這裡我本人推薦採用「全域性安裝」,可以在各個 IDE 上直接填入全域性安裝的 phpcs 可執行路徑。但如果你的版本庫是使用「git」的話,那我推薦使用「專案安裝」,下文就知道原因了。

注: 我使用這種方式「全域性安裝」後,發現每回都關聯不了「VSCode」,這個原因待查。

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

pear

安裝 pear

curl -O https://pear.php.net/go-pear.phar
php -d detect_unicode=0 go-pear.phar
複製程式碼

開始安裝配置,

先選擇 1 (change the Installation Base);

輸入:/usr/local/pear

再選擇 4 (change the Binaries directory),

輸入:/usr/local/bin

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

開始安裝 PHP_CodeSniffer

pear install PHP_CodeSniffer
複製程式碼

在 MacOS 系統下:

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

在 Centos Linux 系統下安裝效果:

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

此方法比較有效果,而且也符合在多系統上嘗試,比如本人同時在「Mac」和 「Linux」下都可以正常安裝和使用。

*注:*我沒在「Windows」環境下嘗試,尚未知道效果。

brew

brew install php-code-sniffer
複製程式碼

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

這種方法顯然在「Mac」系統下有效了!

當然根據官網的文件,還有其他方法,歡迎大家去嘗試:

具體可參考:github.com/squizlabs/P… 中的「Installation」部分。

使用 phpcs

無論是本地還是伺服器,只要我們安裝好了,自然就可以開始使用了。最直觀也是最簡單的方法莫過於用命令列的方式了,如:

phpcs php_path

// or

phpcs php_dir
複製程式碼

但想到我們是用 IDE 寫程式碼的,而且是希望實時看到效果的,所以下面嘗試在幾個 IDE 下看看如何使用。

安裝 VSCode 外掛

在外掛介面,搜尋:phpcs,安裝即可。

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

參考:marketplace.visualstudio.com/items?itemN…

配置外掛

由於專案使用的是系統的 phpcs,所以需要在 user setting 中配置可執行路徑和自己自定義的編寫風格

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

這時候我們去看看我們的程式碼介面,是不是有了 phpcs 的提示了:

![2018-05-10 09.29.08](http://ow20g4tgj.bkt.clouddn.com/2018-05-10-2018-05-10 09.29.08.gif)

安裝 PhpStorm 外掛

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

直接看圖,不需要做過多的說明了。

基本到此,phpcs 的外掛就可以使用了。

版本檢測規範

我們希望在團隊專案程式碼提交版本庫之前「pre-commit」就能檢測 出不符合「PSR-2」 標準的程式碼檔案。無論是 svn 或者 git,都能在「pre-commit」獲取提交版本庫的程式碼檔案,然後再利用「phpcs」去檢測每個檔案是否符合規範。

svn

由於每個 svn 在服務端都有對應 hooks 資料夾,可以在「pre-commit」時,驗證程式碼的規範,直接上檔案,比較好理解:

#!/bin/bash

LOG="/tmp/svn.log"
touch ${LOG}

REPOS="$1"
TXN="$2"
echo "REPOS: $REPOS" > ${LOG}
echo "TXN: $TXN" >> ${LOG}

SVNLOOK="/usr/bin/svnlook"
PHPCS="/usr/bin/phpcs"

# php file extension
PHP_EXT="php"

MSG_MIN_CHAR_NUM=3

MAX_PNG_SIZE=2048

PROHIBITED_FILES=(
)

TMP_DIR="/tmp/svn"
if [[ -d ${TMP_DIR} ]]; then
    rm -r ${TMP_DIR}
fi
mkdir -p ${TMP_DIR}

function check_php_syntax {
local php_file=$1
echo `${PHPCS} ${php_file} 2>&1`
}

function create_file {
local file_name=$1
# Create tmp file and copy content
tmp_file="${TMP_DIR}/${file_name}"
mkdir -p "$(dirname "${tmp_file}")" && touch "${tmp_file}"
${SVNLOOK} cat -t "${TXN}" "${REPOS}" "${file_name}" > ${tmp_file}
}

changed_info_str=`${SVNLOOK} changed -t "${TXN}" "${REPOS}"`
IFS=$'\n' read -rd '' -a changed_infos <<<"${changed_info_str}"

php_error_msg=""
for changed_info in "${changed_infos[@]}"; do
    # Prevent commiting file that contains space in its filename
    echo ${changed_info} >> ${LOG}
    operation=`echo ${changed_info} | awk '{print $1}'`
    if [[ ${operation} = "A" ]] && [[ `echo ${changed_info} | awk '{print NF}'` -gt 2 ]]; then
        echo "Please do not commit file that contains space in its filename!" 1>&2
        exit 1
    fi
    file_name=`echo ${changed_info} | awk '{print $2}'`
    echo "operation: ${operation}, file: ${file_name}, ext: ${ext}" >> ${LOG}

    # Check prohibit-commit files
    for prohibited_file in ${PROHIBITED_FILES[@]}; do
        if [[ ${file_name} = ${prohibited_file} ]]; then
            echo "${file_name} is not allowed to be changed!" 1>&2
            exit 1
        fi
    done

    ext=`echo ${file_name} | awk -F"." '{print $NF}'`

    if [[ ${operation} = "U" ]] || [[ ${operation} = "A" ]]; then
        tmp_file="${TMP_DIR}/${file_name}"

        # Check lua syntax
        if [[ ${ext} = ${PHP_EXT} ]]; then
            echo "Check syntax of ${tmp_file}" >> ${LOG}
            create_file ${file_name}
            error_msg=`check_php_syntax ${tmp_file}`
            if [[ `echo ${error_msg} | sed 's/\n//g'` != "" ]]; then
                php_error_msg="${php_error_msg}\n${error_msg}"
            fi
        fi
    fi
done

rm -r ${TMP_DIR}

if [[ ${php_error_msg} != "" ]]; then
    echo "php error: ${php_error_msg}" >> ${LOG}
        echo "Please fix the error in your php program:${php_error_msg}" 1>&2

    exit 1
fi

exit 0
複製程式碼

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

這就是我們想要看到的效果了,無論 IDE 的實時提示,還是提交程式碼時的檢測反饋,都會告訴我們哪裡格式不符合規範了。

git

這裡主要參考 WickedReports/phpcs-pre-commit-hook github.com/WickedRepor… 的寫法:

#!/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. Code standard PSR2."
    /usr/local/bin/phpcs --standard=PSR2 --colors --encoding=utf-8 -n -p $FILES
    if [ $? != 0 ]
    then
        echo "Fix the error before commit!"
        echo "Run"
        echo "  ./vendor/bin/phpcbf --standard=PSR2 $FILES"
        echo "for automatic fix or fix it manually."
        exit 1
    fi
fi

exit $?
複製程式碼

我們把該檔案內容寫入「.git/hooks/pre-commit」中,然後再提交一個檔案,看看執行效果。

在實驗之前,我們先呼叫本地的 phpcs 外掛,看看我們的一個檔案程式碼的規範情況:

phpcs --standard=PSR2 --encoding=utf-8 -n -p app/Http/Controllers/ApplyController.php
複製程式碼

執行結果提示:

E 1 / 1 (100%)



FILE: /Users/app/Http/Controllers/ApplyController.php
------------------------------------------------------------------------------------------------------
FOUND 4 ERRORS AFFECTING 3 LINES
------------------------------------------------------------------------------------------------------
 17 | ERROR | [x] Opening brace should be on a new line
 60 | ERROR | [x] Opening parenthesis of a multi-line function call must be the last content on the
    |       |     line
 62 | ERROR | [x] Multi-line function call not indented correctly; expected 12 spaces but found 16
 62 | ERROR | [x] Closing parenthesis of a multi-line function call must be on a line by itself
------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 4 MARKED SNIFF VIOLATIONS AUTOMATICALLY
------------------------------------------------------------------------------------------------------

Time: 87ms; Memory: 6Mb
複製程式碼

主要報錯在於:

60 行: output函式

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

17 行:

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

好了,我們執行 git commit 試試:

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

接著我們把這幾個不規範的地方改了之後,同樣執行本地方法,返回結果:

phpcs --standard=PSR2 --encoding=utf-8 -n -p app/Http/Controllers/ApplyController.php
. 1 / 1 (100%)


Time: 44ms; Memory: 6Mb

複製程式碼

我們再執行 git commit 試試:

推薦一款 phpcs 外掛規範 Laravel 程式碼 (規範從本地寫程式碼到版本控制)

完美了!

注:「svn」和「git」的區別在於,svn 是放在伺服器上做「pre-commit」檢測的,而「git」是在本地本專案中的,這也是上文說的,如果你用 git 做版本庫,推薦你用「composer」專案安裝的方式安裝工具。

總結

本文以本地 Mac 系統和伺服器 Linux 系統為安裝端,以 VSCode 和 PHPStorm 兩大主流 IDE 作為使用端,以 svn 和 git 為版本庫為例,較為完整而又系統流程的說一說「phpcs」的使用,希望對大家有所幫助!

最後留個小問題:如果你使用 Docker 和 git,怎麼做這一流程式檢測?

相關文章