静的コード解析ツールのPHPMDを導入した話

# 目的
静的コード解析ツールのPHPMDの使用方法をまとめる。


# What is PHPMD ?

PHPMDを使用すると、次の４つを静的に解析できる。

* 潜在的なバグ
* 準最適な状態のコード
* 複雑になっているコード
* 未使用となっているメソッド、プロパティ

# 環境

* Docker 20.10.12 / Docker Compose 1.29.2　
* php 8.1.7
* phpmd/phpmd 2.12.0
* composer 2.3.7
* macOS Monterey 12.1 (M1チップ)

```:検証時のディレクトリ構造の一部抜粋
src(Docker環境下で/var/www/htmlと定義)
├── vendor
│      └──bin
│      　　　　　　 └── phpmd
├── composer.json          
├── composer.lock            
├── sample.php
└── rulesets.xml

```


# composer経由でPHPMDを導入
※下記では、すでにcomposerが導入され、composer.json、composer.lockファイルが存在していることを前提としました。

composer.jsonファイルに下記を記載し、ターミナルでupdateコマンドを実行する。
```php5:composer.json
    "require-dev": {
        "phpmd/phpmd" : "@stable"
    }
```

```:ターミナル
composer　update phpmd/phpmd
```
※composer updateとしての実行も可能であるが（つまりphpmd/phpmdを書かない）、composer.jsonに記載されている全てが更新対象となり、意図しない部分のバージョンが更新されることも理解して使用する必要がある。

※composer install実行では何も起こらないので注意
(composer.lockファイルがある状況では、あくまでcomposer.lockファイルに記載のパッケージ情報をインストールするという意味になるため。

ちなみに下記公式ドキュメントにおいては、composerを初めて導入した後に導入する想定なので、composer install としての記述がなされている。
https://phpmd.org/download/index.html
）
# 実行コマンド
```:ターミナル
【形式】
phpmdまでのパス 解析対象ファイル・ディレクトリ 出力形式 使用するルール・ファイル名 [オプション]

【例】
<VSCode Remote Development環境下の場合>
./vendor/bin/phpmd . text　unusedcode,naming --suffixes php --exclude vendor

<上記環境ではない場合>
docker compose exec app ./vendor/bin/phpmd . text　unusedcode,naming --suffixes php --exclude vendor


【意味】
カレントディレクトリ以下の全ての.phpファイルを解析し、unusedcode,namingのルールに照らし合わせ、テキスト形式で結果を出力する（ただし、vendor以下ファイルは対象外とする）

```

#### コマンド末尾のオプション指定の例

|オプション|説明|使用例|
| :---: | :---: | :---: |
|--reportfile|結果を記載したファイルを新しく作成し出力する|--reportfile foo.xml|
|--suffixes|対象とするファイルの拡張子を指定する|--suffixes php| 
|--exclude|除外するファイル、ディレクトリを指定|--exclude vendor <br> |

※--exclude vendor/*と指定するとエラーになるので、--exclude vendorが正と思われる。

その他のオプションは公式ドキュメントを参照
https://phpmd.org/documentation/index.html


#### ルールの例

|ルール分類名|概要|ルール例(一部抜粋)|
| :---: | :---: | :---: |
|cleancode|綺麗なコードか|ElseExpression,StaticAccess|
|codesize|制御構造の数、メソッドやプロパティの数など|CyclomaticComplexity,TooManyMethods| 
|controversial|クラス、プロパティのCamelCase記法をチェック（賛否あり）|CamelCaseClassName,CamelCasePropertyName|
|design|ソフトウェアデザイン(子クラスの数、非推奨構文など)|ExitExpression,NumberOfChildren|
|naming|クラスやプロパティ名の長さなど命名規則のチェック|LongClassName,ShortVariable|
|unusedcode|未使用のプロパティやメソッド等のチェック|UnusedPrivateField,UnusedPrivateMethod|

詳細は公式ドキュメントを参照
https://phpmd.org/rules/index.html


# 結果の実例
```php5:sample.php(解析対象ファイル)

1. <?php
2.
3. class Foo
4. {
5.     public function bar()
6.     { $sh = 'short test';
7.       $longlonglonglongolonglong = 'long test';
8.     }
9. }

```

```:ターミナル
※下記は全てVSCode Remote Development環境下での実行である

[実行コマンド]
/var/www/html# ./vendor/bin/phpmd sample.php text naming,unusedcode

ーーーーーーーーーーーーーーーー以下出力結果ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

/var/www/html/sample.php:6  ShortVariable        Avoid variables with short names like $sh. Configured minimum length is 3.
→　変数$shが2文字なので "ShortVariable" で検出(最低3文字以上が必要)

/var/www/html/sample.php:6  UnusedLocalVariable  Avoid unused local variables such as '$sh'.
→　変数$shが宣言後、未使用なので "UnusedLocalVariable" で検出

/var/www/html/sample.php:7  LongVariable         Avoid excessively long variable names like $longlonglonglongolonglong. Keep variable name length under 20.
→ 変数$shが24文字なので "LongVariable" で検出(最大20文字をオーバー)

/var/www/html/sample.php:7  UnusedLocalVariable  Avoid unused local variables such as '$longlonglonglongolonglong'.
→ 変数$longlonglonglongolonglongが宣言後、未使用なので "UnusedLocalVariable" で検出
```

# ルールのカスタマイズ方法

githubのREADME.rstのCommand line usage項目内の While the rulesets.xml ruleset file could look like this:の部分をコピーしてくる。
→https://github.com/phpmd/phpmd

\<rule />で囲まれた部分がルール部分であり、デフォルトでは6種類セットされている。
(既出の表で挙げたものである)

補足：この6種類のルールファイルは、下記ディレクトリに格納されている。
/var/www/html/vendor/phpmd/phpmd/src/main/resources/rulesets/


```xml:rulesets.xml(githubからそのままコピー)
<?xml version="1.0"?>
<ruleset name="My first PHPMD rule set"
         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>
    My custom rule set that checks my code...
  </description>

  <rule ref="rulesets/codesize.xml" />
  <rule ref="rulesets/cleancode.xml" />
  <rule ref="rulesets/controversial.xml" />
  <rule ref="rulesets/design.xml" />
  <rule ref="rulesets/naming.xml" />
  <rule ref="rulesets/unusedcode.xml" />
</ruleset>
```
必要に応じて\<rule />タグに対して下記の編集を行う。

```xml:上記rulesets.xmlを編集
ルール分類にある一部のルールのみを適用する場合は、.xmlの後ろにルール名を追記する
<rule ref="rulesets/naming.xml/LongVariable" />

ルール分類にある一部のルールのみを除外する場合は、<exclude />タグでネストしてルール名を記載し、</rule>タグで閉じる
<rule ref="rulesets/naming.xml">
  <exclude name="LongVariable" />
</rule>
```
参考　https://phpmd.org/documentation/creating-a-ruleset.html

# 検証ファイルそのものに除外ルールを記載する方法
コメントアウトの意味ではないので注意する。
またこれを記載した直下のクラスにだけ適用され、その後に出てくるクラスには適用されないようなので、有効範囲に注意が必要。(間違っていたらすみません)


```php5:任意の.phpファイル
全てのルールを除外
/**
* @SuppressWarnings(PHPMD)
 */

ある一部のルールを除外
/**
* @SuppressWarnings(PHPMD.LongVariable)
 */

```
参考　https://phpmd.org/documentation/suppress-warnings.html

# 参考
https://github.com/phpmd/phpmd
https://phpmd.org/
[phpmdのルール一覧](https://qiita.com/rana_kualu/items/097db09f711fe15eddb7)
[PHPの静的コード解析ツール『PHPMD』を使ってみた。](https://qiita.com/yuji0602/items/28b0c2363bae8fce055a)