博客 / 詳情

返回

PHP Linter 與規則定義小探

本文旨在確認 PHPLint、PHP-CS-Fixer 和 PHP_CodeSniffer 的差異,根據當前公司的需求進行選擇,並編寫一個簡單的自定義規則。

Lint 工具淺析

PHPLint 是三者中我唯一用過的 Lint 工具,另外兩個則是較為出名的 PHP 領域的自動化語法規範或靜態錯誤定位工具。

每個工具都在安裝、運行的基礎上,測試三塊:

  1. 展示錯誤,自動修復(如果有這個功能)
  2. 某一行代碼跳過特定檢測
  3. 切換規則集合。
  4. 關閉一條規則。
  5. 寫一個自定義規則。

參考開源社區的活躍程度,本次主要的探索點也會放在後兩者上。

PHP-CS-Fixer

環境説明,PHP 7.4 是它所需的最低版本。

提一嘴:請保證你的 PHP 最少處於 LTS 版本中,否則它將失去後續的安全補丁。

它支持多種安裝方式:PHAR、Composer 等等。

通過 composer require friendsofphp/php-cs-fixer 安裝,然後跑起來:vendor/bin/php-cs-fixer fix some_path

如果你裝在全局,就不需要調用 vendor 目錄的執行文件,不過考慮一下規範是否是全局的,否則寫個 Shell 更合適。

展示錯誤,自動修復

以我寫的 Laravel Demo 為例:vendor/bin/php-cs-fixer fix app/Http/,成功將我的 if (expression) once_line_code 給轉化掉了,沒啥毛病。

使用 vendor/bin/php-cs-fixer fix app/Http --dry-run --diff,這樣不會修改文件,並能獲取檢測結果的具體問題和推薦修改。

某一行代碼跳過特定檢測

基於 Issue 4512,暫不支持。

切換規則集合

通過 /.php-cs-fixer.dist.php,你可以方便的定義自己的配置文件——裏面涵蓋了具體規則、規則集合、待檢查的文件目錄、剔除檢查的目錄等的變更。

簡單的嘗試:替換為 PSR12 並開啓不安全修復模式,沒問題。

關閉某條規則

這個工具採用的是最小規則默認,所以在配置文件中不包含,就可以放棄對應規則的檢測。

CI

官方提供了對應的命令,使你高效的將其納入 CI 構建流程。

編寫自定義規則

嘗試添加了一條 App/test_rule 規則。優點如下:

  1. 編寫簡單,在接口約束的前提下,基本上都是可讀的 Function 編寫;
  2. 規則存放靈活,規則類可以直接放在任意項目路徑下——寫個包隔離出去也可以,如果你喜歡;
  3. 參考性強,儘管缺乏足夠的自定義規則編寫指南,但你很容易就找到了所有的規則類,閲讀、理解、編程,很好的循環。
  4. 基本上遵循 \PhpCsFixer\Fixer\FixerInterface 接口即可,更多的自行參考文檔。

PHP_CodeSniffer

環境説明,PHP 5.4 是它所需的最低版本,但特定規則需要更高版本的 PHP 支持。

與 CS-Fixer 類似,也提供了多種安裝方式,老樣子:

composer require "squizlabs/php_codesniffer=*"

展示錯誤,自動修復

以我寫的 Laravel Demo 為例:./vendor/bin/phpcs app/Http/

顯示出了密密麻麻的錯誤數量提示:Line | Level | Message。很好,有 PHPLint 的味道了。

它的一個工具:phpcbf 可以讓你自動修復發現的問題——如果能自動修復的話。

如果我們希望接入 CI,PHP CS 提供了對應的 Action,你可以將其融入到構建流程。

某一行代碼跳過特定檢測

CS 提供了跳過文件、跳過某行、暫時關閉,乃至下一行不檢測某個規則。

簡單測試了上述的,譬如通過 // phpcs:ignore,可以跳過某行檢測,從文檔裏你可以找到更多、更詳細的解釋。

切換規則集合

通過 .phpcs.xml 或類似的文件(它竟然支持數個不同的名字……),我們可以定義配置:

<rule ref="/path/to/standards/Generic/Sniffs/Commenting/TodoSniff.php"/>

這樣就添加了對應的 ToDo Sniff 規則。

關閉某條規則

與 CS Fixer 一致,只要不包含對應規則即可。我們感受到它默認支持很多規則,是因為它的默認配置比較全面。

命名相較 CS Fixer 更簡單,但規則列表比較難找。

CI

PHP CS 官方支持的是 Github CI Action,對於分發和 diff 需求,並沒有合適的支持。

儘管如此,你可以通過 diffFilter 為其提供基於 commit 或 PR 範圍的合理提示。

嘗試了一下,調整 Shell 和鈎子相比較 PHP CS Fixer 略微耗時。

編寫自定義規則

CS 將規則稱呼為嗅探器(Sniff),支持 PSR2、PSR12 等通用規則。感覺規則比 Fixer 提供的少一些,不過沒關係,我們來嘗試添加了一條 test_rule 規則。

首先指出,CS 有完整地 Step-To-Step 教程指導你如何寫自己的規則,如果你希望使用 CS 在自己的技術棧中,推薦直接閲讀它(官方的 Wiki 指南),而不是我這樣的博客文章。

開工。

首先,讓我們像 Fixer 一樣,在項目中直接創建自己的規則目錄和文件,保持靈活的同時,後期依舊可以將其抽離為 Composer Package。

我們管容納自定義規則集合、定義文件等的目錄叫 MyStandard,進入其中並創建 ruleset.xml——它可以定義規則具體的條件。

格式大略為:

<?xml version="1.0"?>
<ruleset name="MyStandard">
  <description>A custom coding standard.</description>
</ruleset>
所以,這個規則就被稱為 MyStandard 規則集,裏面將包含一個或若干的實際嗅探器來執行檢查。

接着創建目錄 MyStandard/Sniffs,為了不讓你困惑,我直接用嵌套結構表示它的實際位置。這裏將容納嗅探器文件。

然後我們對評論進行嗅探,繼續創建目錄 MyStandard/Sniffs/Commenting,在其中創建 DisallowHashCommentsSniff.php 嗅探器(文件)。

Sniffs 下的目錄其實不重要,只是為了方便我們將更多嗅探器區分開,你如果喜歡,也可以將百十來個規則丟在一起,實現軟件工程師們深惡痛絕的“大雜燴”(:D)。

與 Fixer 一樣,它也規定了一個接口——Only Once,PHP_CodeSniffer\Sniffs\Sniff,繼承並實現關鍵方法就好。

  • supportedTokenizers 屬性定義:嗅探的文件類型,PHP、JS、Rust、etc.
  • register 方法定義:嗅探的代碼類型,單行註釋、多行註釋、單行代碼、代碼塊、etc.
  • process 方法定義:實際的錯誤檢查、處理。

    • 系統提供了 addError / addWarning 作為報錯信息的添加方法。
    public function process(File $phpcsFile, $stackPtr)
    {
        $tokens = $phpcsFile->getTokens();
        if ($tokens[$stackPtr]['content']{0} === '#') {
            $error = 'Hash comments are prohibited; found %s';
            $data  = array(trim($tokens[$stackPtr]['content']));
            $phpcsFile->addError($error, $stackPtr, 'Found', $data);
        }
    }

最終,我們採用 phpcs --standard=/path/to/MyStandard has_error_code.php 嘗試檢測一個 has_error_code.php 文件,裏面包含了一些錯誤。

/path/to/MyStandard 定義了我們規則集合的位置,以幫助 CS 尋找包外的規則集。

試試看吧!

最後,嗅探的文件類型不支持 Rust,那是開玩笑的——儘管你可以讓它支持——這很詭異。
無論怎樣,更喜歡哪個工具,都祝你玩的愉快!
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.