如何提升程式碼品質?(一個實際的方法)
我寫這篇文章的靈感源於 Laravel 作者 Taylor Otwell 的一篇文章,比較各框架的程式碼品質,儘管 Taylor Otwell 對分析結果的詮釋引起不同意見的討論,該文章仍提供我新的角度看待專案的程式碼品質。
本文首先會介紹幾項程式碼品質分析工具與其安裝流程,並在報表解讀中說明分析報告所使用的指標與我對這些結果的看法。
分析工具
PHP 程式碼分析工具很多,但每個工具的參數選項不同,需要花費不少時間安裝與設定,我推薦用 phpqa 這個套件整合了多個常用工具,預設安裝了:
- phpmetrics:以各種指標分析 PHP 專案與類別
- phploc:測量並分析 PHP 專案的結構與大小
- phpcs:檢查違反標準的程式碼
- phpmd:檢查效率低下的程式碼或不良的編程習慣,類似 Java PMD
- phpcpd:檢查重複的程式碼
- pdepend:以各種指標衡量專案的品質,並有助於找出應該重構的部分
另外能自定義安裝的套件還有:parallel-lint、phpstan、phpunit、psalm、security-checker、deptrac
phpqa 的使用方式,先透過 composer 安裝:
composer global require edgedesign/phpqa --update-no-dev
確認安裝成功,看一下預設安裝的工具有哪些:
phpqa tools
接著就能用 phpqa 產生報表,用 --analyzedDirs
參數指定要分析的目錄:
phpqa --analyzedDirs src,tests --report
執行結果:
結果會存放在 build 資料夾內,打開 build/phpqa.html 就能看到分析報表。
報表解讀
打開 build/phpqa.html,會看到報表的上方有一排頁籤,點進去能看到各個工具的分析結果。
phpmetrics report:包含多個分析指標,例如相依物件的圖表化、程式碼大小與複雜度分析,詳細內容可以在文件查到各指標的意義與算法。
例如首頁的泡泡圖,每個泡泡是一個 PHP 檔,顏色代表維護性,越紅表示維護性越差,而大小則代表複雜度,越大表示越複雜;觀察到專案內可維護性較差且較複雜的是 Repository 相關類別,再來是 Service 相關類別,能看得出此專案以 Repository Pattern 架構的的複雜度主要堆積之處。
phploc report:分析程式碼的大小與結構,包含行數、類別及方法的最大長度與平均長度、註解佔的比例等,另外也有循環複雜度與依賴性的分析結果,但沒有細分成各類別,僅有專案整體的分析
phpcs report:顯示出不符合 PSR2 規範的檔案;因為平時開發流程已經把 PHP_CodeSniffer 整合在 commit 與 CI/CD,所以這個報表非常的乾淨 😌
phpmd report:條列不符合規則的 PHP 檔案,詳細規則可以參考此文件
phpcpd report:會列出程式碼相似程度過高的檔案
以本專案而言出現最多的錯誤是:
- CamelCaseVariableName:不符合 Camel Case 的變數都會被掃描出來,因為此專案會將 foreach 中沒用到的 iterable_expression 命名為
$_
以提升效能,這個錯誤大部分屬於此情況 - UnusedLocalVariable:和 1 的問題一樣,大部分是
$_
被判斷為未使用的變數 - CyclomaticComplexity:複雜度是由方法中的分支、循環語句決定,包括 if、while、for 與 case,phpmd 中 1–4 是低複雜度,5–7 表示中等複雜度,8–10 是高複雜度,11+ 是非常高複雜度,超過 10 的會被掃瞄出來;本專案被掃瞄出來的幾乎都是由於方法過長,且包含太多邏輯,這些錯誤可以作為一個重構的參考清單
- UnusedFormalParameter:方法的參數未被使用,可能是修改方法後忘記刪除沒用的參數
pdepend report:報表有二個圖用來暸解分析結果
第一張圖 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 偏高,表示對於有效程式碼數量而言,方法數量偏少;而前面其他分析結果中能得知方法的平均行數並不高,代表可能有部分巨大的方法包含了太多程式碼
- 複雜度指標詳細資訊請參考此文件
第二張圖則透過幾個因子計算出,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,歡迎成為我們的夥伴!