如何提升程式碼品質?(一個實際的方法)

MarcusJian
AsiaYo Engineering
Published in
7 min readNov 1, 2022
PhpMetrics report

我寫這篇文章的靈感源於 Laravel 作者 Taylor Otwell 的一篇文章,比較各框架的程式碼品質,儘管 Taylor Otwell 對分析結果的詮釋引起不同意見的討論,該文章仍提供我新的角度看待專案的程式碼品質。

本文首先會介紹幾項程式碼品質分析工具與其安裝流程,並在報表解讀中說明分析報告所使用的指標與我對這些結果的看法。

分析工具

PHP 程式碼分析工具很多,但每個工具的參數選項不同,需要花費不少時間安裝與設定,我推薦用 phpqa 這個套件整合了多個常用工具,預設安裝了:

  1. phpmetrics:以各種指標分析 PHP 專案與類別
  2. phploc:測量並分析 PHP 專案的結構與大小
  3. phpcs:檢查違反標準的程式碼
  4. phpmd:檢查效率低下的程式碼或不良的編程習慣,類似 Java PMD
  5. phpcpd:檢查重複的程式碼
  6. pdepend:以各種指標衡量專案的品質,並有助於找出應該重構的部分

另外能自定義安裝的套件還有:parallel-lintphpstanphpunitpsalmsecurity-checkerdeptrac

phpqa 的使用方式,先透過 composer 安裝:

composer global require edgedesign/phpqa --update-no-dev

確認安裝成功,看一下預設安裝的工具有哪些:

phpqa tools

phpqa tools

接著就能用 phpqa 產生報表,用 --analyzedDirs 參數指定要分析的目錄:

phpqa --analyzedDirs src,tests --report

執行結果:

phpqa result

結果會存放在 build 資料夾內,打開 build/phpqa.html 就能看到分析報表。

報表解讀

打開 build/phpqa.html,會看到報表的上方有一排頁籤,點進去能看到各個工具的分析結果。

phpmetrics report:包含多個分析指標,例如相依物件的圖表化、程式碼大小與複雜度分析,詳細內容可以在文件查到各指標的意義與算法。

例如首頁的泡泡圖,每個泡泡是一個 PHP 檔,顏色代表維護性,越紅表示維護性越差,而大小則代表複雜度,越大表示越複雜;觀察到專案內可維護性較差且較複雜的是 Repository 相關類別,再來是 Service 相關類別,能看得出此專案以 Repository Pattern 架構的的複雜度主要堆積之處。

Maintainability / Complexity

phploc report:分析程式碼的大小與結構,包含行數、類別及方法的最大長度與平均長度、註解佔的比例等,另外也有循環複雜度與依賴性的分析結果,但沒有細分成各類別,僅有專案整體的分析

phpcs report:顯示出不符合 PSR2 規範的檔案;因為平時開發流程已經把 PHP_CodeSniffer 整合在 commit 與 CI/CD,所以這個報表非常的乾淨 😌

phpmd report:條列不符合規則的 PHP 檔案,詳細規則可以參考此文件

phpcpd report:會列出程式碼相似程度過高的檔案

以本專案而言出現最多的錯誤是:

  1. CamelCaseVariableName:不符合 Camel Case 的變數都會被掃描出來,因為此專案會將 foreach 中沒用到的 iterable_expression 命名為 $_ 以提升效能,這個錯誤大部分屬於此情況
  2. UnusedLocalVariable:和 1 的問題一樣,大部分是 $_ 被判斷為未使用的變數
  3. CyclomaticComplexity:複雜度是由方法中的分支、循環語句決定,包括 if、while、for 與 case,phpmd 中 1–4 是低複雜度,5–7 表示中等複雜度,8–10 是高複雜度,11+ 是非常高複雜度,超過 10 的會被掃瞄出來;本專案被掃瞄出來的幾乎都是由於方法過長,且包含太多邏輯,這些錯誤可以作為一個重構的參考清單
  4. UnusedFormalParameter:方法的參數未被使用,可能是修改方法後忘記刪除沒用的參數

pdepend report:報表有二個圖用來暸解分析結果

Pyramid Overview

第一張圖 Pyramid Overview 顯示了規模、複雜度、耦合性三方面資料,並提供參考值來衡量專案的品質。

規模

  • 74215 行有效程式碼(LOC)
  • 127 個 packages
  • 869 個類別
  • 4095 個方法

複雜度:

  • ANDC 是子類平均數量指標,比如系統有 10 個類別,ANDC 值 0.5 表示其中 5 個類別繼承另外 5 個類別,ANDC 值越高表示複雜度越高
  • AHH 是平均繼承深度,比如有 10 個類別,AHH 值 1,可以用不同的方式解釋 ,一是 5 個類別繼承另外 5 個類別,或者其中 5 個類從 1 個根類別垂直繼承下來,繼承層數越多就越複雜
  • LOC/NOM 值 18.123 偏高,表示對於有效程式碼數量而言,方法數量偏少;而前面其他分析結果中能得知方法的平均行數並不高,代表可能有部分巨大的方法包含了太多程式碼
  • 複雜度指標詳細資訊請參考此文件
Abstraction Instability Chart

第二張圖則透過幾個因子計算出,Instability(不穩定度)與 Abstractness(抽象度)二個維度:

  • Ca(Afferent Couplings):有多少其他 package 的類別依賴該 package 中的類別;值越高代表該 package 改動會對其他 package 造成更大影響
  • Ce(Efferent Couplings):package 中的類別依賴多少其他 package 的類別;值越高代表該 package 對其他 package 的修改更敏感
  • Instability:Ce / (Ce + Ca),值越接近 0 表示越穩定
  • Abstractness:由抽象類 (ac) 數量與所有類別 (cc) 數量計算,計算公式為 ac / (ac + cc),值越接近 0 表示這個 package 中的類別都是非抽象的,而值越接近 1 則表示一個 package 只有介面與抽象類別

越靠近綠色斜線表示 Instability 與 Abstractness 的平衡程度越好,由此可以看出此專案大部分 package 並不抽象,另外 Instability 超過 0.5 的相當密集,表示 package 有許多互相依賴,更多資訊請參考此文件

結論

了解一個專案可以從很多角度,比如產品的功能,核心功能的實作方式;或是從本文介紹的幾種分析工具,根據輸出的報表結果,特別是複雜度相關的指標,了解這些部分被呼叫的情況,進而從這些報表中找出有問題的地方進行重構,希望本文的介紹能提供大家一些靈感,以另一種角度來觀察、提升專案的程式碼品質。

如果你對本文有任何想法,請留言讓我知道,也歡迎按讚、分享。

最後,AsiaYo 目前在招募 Sr. Product Manager,詳細資訊請參考 CakeResume,歡迎成為我們的夥伴!

--

--