PHP code conventions checking tools

Tổng quan bài viết:
1. Static Code Analysis là gì
2. PHP Linter
3. PHP_CodeSniffer
4. PHP Mess Detector
5. PHPStan
6. Tài liệu tham khảo


Static Code Analysis là gì

Static code analysis (SCA) là việc thực hiện phân tích mã nguồn để đảm bảo về convention của dự án một cách tự động, hay để đánh giá chất lượng mã nguồn, hoặc tìm ra các lỗi tiềm ẩn, các lỗi thường gặp mà không phải chạy mã nguồn đó, ngăn các lỗi xảy xa khi deploy sản phẩm. Nó cũng gần giống việc compile mã nguồn ra mã máy đối với các ngôn ngữ dạng biên dịch như C/C++, Java... Tuy nhiên PHP là ngôn ngữ thông dịch nên mặc định không có bước compile này. Cách thức hoạt động của SCA là đọc và phân tích cấu trúc của bộ mã nguồn sau đó đối chiếu với các quy tắc đã được đề ra xem mã nguồn đó có vi phạm lỗi nào không. SCA thường được áp dụng vào quá trình test tự động hoặc tích hợp vào các hệ thống Continuous Integration trong quá trình build, merging code và deploy. Sau đây mình sẽ giới thiệu một số công cụ SCA thường dùng.


PHP Linter

Đây là tool đơn giản, được tích hợp sẵn trong PHP CLI, chỉ dùng để kiểm tra các lỗi về cú pháp. VD:

<?php
// php_linter.php
echo 'Hello world;'
$ php -l php_linter.php

Kết quả:

PHP Parse error:  syntax error, unexpected end of file, expecting ',' or ';' in php_linter.php on line 3
Errors parsing php_linter.php

Tool này thường được tích hợp vào các editor như Sublime Text hoặc VSCode để phát hiện lỗi cú pháp tự động.


PHP_CodeSniffer

PHP_CodeSniffer (PHPCS) là công cụ thiết yếu để đảm bảo convention của dự án. Nó cung cấp 2 công cụ chính:

  1. phpcs: được dùng để tìm ra những lỗi sai coding convention.
  2. phpcbf: tự động fix các lỗi convention.

Tùy từng dự án mà có thể sử dụng các bộ quy tắc khác nhau, một số bộ quy tắc hay được dùng đó là: PEAR, PSR1, PSR2, Wordpress...

Cài đặt thông qua composer:

$ composer global require "squizlabs/php_codesniffer=*"

Lưu ý:
Trước khi có thể chạy command phpcs chúng ta cần phải thêm đường dẫn đến thư mục composer bin vào PATH của hệ thống
PATH hiểu nôm na là nơi khai báo danh sách các folder có chưa command có thể chạy từ bất cứ đâu trên terminal
Để lấy đường dẫn đến thư mục composer bin:

$ composer global config bin-dir --absolute
Changed current directory to /home/ubuntu/.config/composer
/home/ubuntu/.config/composer/vendor/bin

=> Thư mục composer bin sẽ là:

/home/ubuntu/.config/composer/vendor/bin

Tùy thuộc vào shell bạn đang dùng là bash hay zsh thì bạn có thể thêm đường dẫn vào PATH bằng cách sửa file ~/.bashrc hoặc ~/.zshrc tương ứng:

$ export PATH="/home/ubuntu/.config/composer/vendor/bin:$PATH"

Nhớ khởi động lại terminal hoặc update lại shell bằng lệnh

$ source ~/.bashrc

Hoặc với zsh

$ source ~/.zshrc

Mặc định, PHPCS sẽ tìm kiếm các bộ quy tắc trong thư mục src/Standards (version > 3). Để xem danh sách các bộ quy tắc đã có, chạy lệnh:

$ phpcs -i

Thực hiện analysis:

$ phpcs --standard=<bộ_quy_tắc> <đường_dẫn_file_hoặc_folder> [--report=<định_dạnh_output>]
  • Bộ quy tắc có thể là tên các bộ quy tắc đã được cài sẵn hoặc đường dẫn đến file tự định nghĩa các quy tắc dạng xml
  • Định dạng output có thể là: full (output dạng bảng, mặc định), xml, json, diff...
  • Các tùy chọn khác các bạn xem thêm trên document hoặc trang help phpcs -h

VD ouput dạng bảng:

FILE: /tmp/test.php
---------------------------------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
---------------------------------------------------------------------------------------------------------
 2 | ERROR | [x] Inline control structures are not allowed
---------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------------------------

Time: 46ms; Memory: 6MB

Với tham số -s bạn sẽ biết được error là do vi phạm rule nào: phpcs --standard=VNLABPHPCS -s /tmp/test.php:

FILE: /tmp/test.php
---------------------------------------------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 1 LINE
---------------------------------------------------------------------------------------------------------
 2 | ERROR | [x] Expected 0 spaces after opening bracket; 1 found
   |       |     (PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace)
 2 | ERROR | [x] Expected 0 spaces before closing bracket; 1 found
   |       |     (PSR2.ControlStructures.ControlStructureSpacing.SpaceBeforeCloseBrace)
---------------------------------------------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
---------------------------------------------------------------------------------------------------------

Time: 45ms; Memory: 6MB

Ngoài ra, có 1 tool khác để fix tự động đó là php-cs-fixer (example config với php-cs-fixer).

VD file tự định nghĩa các quy tắc, ngoài khai báo rule, chúng ta cũng có thể setting các tham số, config cho phpcs:

<!-- phpcs.xml -->
<?xml version="1.0" ?>
<ruleset name="VNlab PHP Coding Standard">

    <!-- Add PSR1 standard -->
    <rule ref="PSR1" />

    <!-- Add PSR2 standard -->
    <rule ref="PSR2" />

    <rule ref="PEAR.Commenting.FunctionComment" />
    <rule ref="Generic.Commenting.DocComment" />
    <rule ref="Generic.Strings.UnnecessaryStringConcat" />
    <rule ref="Generic.PHP.LowerCaseConstant" />
    <rule ref="Generic.PHP.LowerCaseKeyword" />
    <rule ref="Generic.PHP.DisallowShortOpenTag" />
    <rule ref="Generic.PHP.DeprecatedFunctions" />
    <rule ref="Generic.NamingConventions.UpperCaseConstantName" />
    <rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
    <rule ref="Generic.Formatting.SpaceAfterCast" />
    <rule ref="Generic.Formatting.DisallowMultipleStatements" />
    <rule ref="Generic.WhiteSpace.DisallowTabIndent" />
    <rule ref="Generic.WhiteSpace.ScopeIndent" />
</ruleset>

=> Thực hiện kiểm tra:phpcs --standard=phpcs.xml file.php

Lưu ý: Nếu bạn đặt tên file là phpcs.xml thì chỉ cần chạy phpcs file.php phpcs sẽ tự động sử dụng file phpcs.xml. Xem thêm ở đây.

Ngoài ra, công cụ này cũng có thể được tích hợp vào Sublime Text thông qua plugin https://packagecontrol.io/packages/Phpcs hoặc PHPStorm hoặc VSCode.


PHP Mess Detector


PHP Mess Detector (PHPMD) có thể phát hiện ra rất nhiều bug tiềm tàng trong mã nguồn. Nó bao gồm các tính năng:

  • Phát hiện lỗi tiềm ẩn
  • Tối ưu code
  • Phát hiện các đoạn code quá phức tạp
  • Phát hiện các tham số thừa, thuộc tính, phương thức không được sử dụng đến

Một số quy tắc hay được dùng nhất là Cyclomatic Complexity - Độ phức tạp của functionđộ dài tối đa của 1 function.

Cài đặt:

$ wget -c http://static.phpmd.org/php/latest/phpmd.phar

Hoặc thông qua composer:

$ composer global require phpmd/phpmd

Cách sử dụng:

phpmd <tên_file_hoặc_thư_mục_src> <định_dạng_kết_qủa_trả_về> <ruleset1,ruleset2,...>
  • Định dạng kết quả trả về có thể là xml, text hoặc html
  • Có thể kết hợp nhiều ruleset cùng 1 lúc ngăn cách bởi dấu ,. Các bộ ruleset mặc định gồm: cleancode, codesize, controversial, design, naming, unusedcode.


Ngoài ra có thể tự định nghĩa ruleset bằng file xml. VD: phpmd.xml

<?xml version="1.0"?>
<ruleset name="PHPMD Ruleset"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">

    <description>PHPMD ruleset</description>


    <rule ref="rulesets/codesize.xml">
        <exclude name="TooManyPublicMethods" />
    </rule>

    <rule ref="rulesets/controversial.xml">
    </rule>

    <rule ref="rulesets/unusedcode.xml"/>

    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable" />
        <exclude name="ShortMethodName" />
        <property name="checkParameterizedMethods" value="true" />
    </rule>

    <rule ref="rulesets/naming.xml/ShortVariable">
        <properties>
            <property name="exceptions" value="id,e,to" />
            <property name="minimum" value="3" />
        </properties>
    </rule>

    <rule ref="rulesets/naming.xml/ShortMethodName">
        <properties>
            <property name="exceptions" value="to" />
            <property name="minimum" value="3" />
        </properties>
    </rule>

    <rule ref="rulesets/codesize.xml/TooManyPublicMethods">
        <properties>
            <property name="maxmethods" value="17"/>
        </properties>
    </rule>

</ruleset>

Không phải lúc nào cũng có thể áp dụng được tất cả các rule, nhưng có thể đọc về các rule đó để biết tại sao ở đây mình làm thế mà họ lại không.

=> http://phpmd.org/rules/index.html


PHPStan

PHPStan là công cụ khá nổi tiếng trong thời gian gần đây khi PHP 7 ra đời với tính năng strongly-typed được support đầy đủ hơn, cũng như khả năng mở rộng qua extensions support đầy đủ cho các framework như Laravel, Symfony...

Chúng ta có thể thử xem cách hoạt động của PHPStan tại trang playground: https://phpstan.org/r/d24f932d-5b0b-4749-8a41-e43720f7c21f

Một số chức năng chính:

  • Kiểm tra syntax
  • Kiếm tra sự tồn tại của classes, methods, functions và constants
  • Kiểm tra sự tồn tại của biến
  • PHPDoc được viết chính xác parameter type và return type
  • Kiểm tra tham số hoặc biến không được dùng đến
  • Kiểm tra đúng kiểu dữ liệu (strict type)

Tool này mình có áp dụng vào 1 dự án Laravel và thấy nó hữu ích nhất là phát hiện những class chưa được import vào (thiếu use)


Tài liệu tham khảo

Trên đây là một số công cụ mình đã sử dụng trong các dự án đã từng làm. Ngoài những công cụ nêu trên còn có một số công cụ khác mình chưa có cơ hội để sử dụng có thể nhắc đến như là :

  • PHP Copy/Paste Detector : được dùng để phát hiện những đoạn code trùng lặp trong các file, nghi vấn là do copy / paste từ file này sang file khác, có thể vi phạm nguyên tắc DRY.
  • Psalm: psalm được phát triển bởi Vimeo trong bối cảnh năm 2015 thì khối lượng codebase của Vimeo đã trở nên vô cùng lớn, phục vụ hàng triệu requests mỗi giờ.
  • PHPQA: PHPQA là tool tích hợp các công cụ trên vào command duy nhất và format lại ouput, giúp bạn không phải cài riêng từng tool.


Ref:
- https://phpmd.org/
- https://github.com/squizlabs/PHP_CodeSniffer
- https://github.com/phpstan/phpstan

Hi vọng sau bài viết này có thể giúp ích được một phần nào đó về vấn đề Analysis cho anh em Coder chúng ta.
Cảm ơn tất cả mọi người!