PHP静的解析ツール PHPMD 個人的メモ

# PHPMD

[PHPMD公式サイト](https://phpmd.org/)

+ 考えられるバグ
+ 最適でないコード
+ 複雑すぎる表現
+ 未使用のパラメーター、メソッド、プロパティ

を通知してくれる
「最適でないコード」「複雑すぎる表現」あたりはPHPStanと差別化出来ているのかな。

```bash
# ./vendor/bin/phpmd [パス] [フォーマット] [オプション]

# example
./vendor/bin/phpmd app/Http/Controllers/TopController.php text codesize,design
```

## インストール

```bash
composer require --dev phpmd/phpmd
```

:::note info
composer以外でのインストール方法は公式サイトを参照してください
:::


## フォーマット

ターミナルで実行してみる分には個人が見やすい形式でOK。
PHPMD を組み込んでシェルでゴニョゴニョしたい場合は、扱いやすい形式でどうぞ。

| オプション | 説明 |
|:-|:-|
| ansi | ansi 形式で結果を出力 |
| html | html 形式で結果を出力 |
| json | json 形式で結果を出力 |
| text | text 形式で結果を出力 |
| xml | xml 形式で結果を出力 |

## オプション

オプションはカンマ区切りで複数指定可能

```bash
# example
cleancode,codesize,design,naming
```

| オプション | 説明 |
|:-|:-|
| cleancode | クリーンなコードベースを強制するルール |
| codesize | 循環的複雑度などコードサイズ関連部分を検出するルール |
| controversial | キャメルケースなど議論の余地のある部分を検出するルール |
| design | ソフトの設計関連の問題を検出するルール |
| naming | 長すぎたり、短すぎたりする名前を検出するルール |
| unusedcode | 使われていないコードを検出するルール |


### cleancode ルール説明

| オプション | 説明 |
|:-|:-|
| BooleanArgumentFlag | boolean フラグの引数は、単一責任原則 (SRP) に違反する場合の信頼できる指標となります。boolean フラグのロジックを独自のクラスやメソッドに抽出することで、この問題を解決することができます。 |
| ElseExpression | else分岐のあるif式は基本的に必要ない。条件を書き換えることで、else節が不要になり、コードがよりシンプルになります。そのためには、いくつかの小さなメソッドにコードを分割する必要があるかもしれませんが、早い段階でreturn文を使ってください。非常に単純な代入の場合、三項演算を使うこともできます。 |
| StaticAccess | 静的アクセスは、他のクラスと交換不可能な依存関係を引き起こし、テストしにくいコードにつながります。静的アクセスは絶対に避け、コンストラクタで依存性を注入するようにしましょう。静的アクセスが許容される唯一のケースは、ファクトリーメソッドに使用する場合です。 |
| IfStatementAssignment | if 節などでの代入は、コードの臭いとみなされます。PHPの代入は、右のオペランドを結果として返します。多くの場合、これは予期された動作ですが、特に右のオペランドがゼロやヌル、 あるいは空の文字列となる場合、発見が困難な多くのバグにつながる可能性があります。 |
| DuplicatedArrayKey | 配列リテラルで同じキーに対して別の値を定義すると、前のキー/値が上書きされるため、事実上未使用のコードとなります。もしキーが別の値を持つことが最初からわかっている場合は、通常、最初の値を定義する意味はありません。 |
| MissingImport | ファイルに含まれるすべての外部クラスをuse文によってインポートすることで、外部クラスを明確に表示することができます。 |
| UndefinedVariable | 以前に定義されていない変数が使用されたときに検出されます。 |
| ErrorControlOperator | エラーの抑制は可能な限り避けるべきです。なぜなら、単にエラーを抑制するだけでなく、発生が予測されないエラーも抑制してしまうからです。さらに、コードの実行速度が低下する可能性もあります。error_reporting() のレベルを変えるか、独自のエラーハンドラを設定することを検討してください。 |

### codesize ルール説明

| オプション | 説明 |
|:-|:-|
| CyclomaticComplexity | 複雑さは、メソッド内の決定点の数にメソッドエントリーの数を加えた数で決まります。決定ポイントは、'if', 'while', 'for', 'case labels'です。一般に、1-4は低複雑度、5-7は中程度の複雑度、8-10は高複雑度、11+は非常に高い複雑度を示す。 |
| NPathComplexity | あるメソッドのNPath複雑度は、そのメソッドを通る非周期的な実行パスの数である。一般に、200の閾値は、複雑さを軽減するために対策を講じるべきポイントと考えられています。 |
| ExcessiveMethodLength | このルールに違反した場合、通常、そのメソッドの処理量が多すぎることを示します。ヘルパーメソッドを作成したり、コピー＆ペーストしたコードを削除したりして、メソッドのサイズを小さくするようにしましょう。 |
| ExcessiveClassLength | 長いパラメータリストは、多数のパラメータをラップするために新しいオブジェクトを作成する必要があることを示す場合があります。基本的には、パラメータをグループ化するようにしましょう。 |
| ExcessiveParameterList | 長いパラメータリストは、多数のパラメータをラップするために新しいオブジェクトを作成する必要があることを示す場合があります。基本的には、パラメータをグループ化するようにしましょう。 |
| ExcessivePublicCount | クラス内に多数のパブリックメソッドや属性が宣言されている場合、そのクラスを徹底的にテストするために多くの労力が必要となるため、クラスを分割する必要がある可能性があることを示します。 |
| TooManyFields | フィールドが多すぎるクラスは、情報の一部をオブジェクトに入れ子にしてグループ化することで、より少ないフィールドに設計し直すことができます。例えば、city/state/zipフィールドを持つクラスは、代わりにAddressフィールドを1つ持つことができます。 |
| TooManyMethods | メソッドが多すぎるクラスは、おそらくリファクタリングの対象となり、その複雑さを軽減し、より細かいオブジェクトを持つ方法を見つけることができます。デフォルトでは、'get' や 'set' で始まるメソッドは無視されます。PHPMD 2.3 で、このデフォルトが 10 から 25 に変更されました。 |
| TooManyPublicMethods | パブリックメソッドが多すぎるクラスは、おそらくリファクタリングの対象となり、その複雑さを軽減し、より細かいオブジェクトを持つ方法を見つけることができます。デフォルトでは、'get' や 'set' で始まるメソッドは無視されます。 |
| ExcessiveClassComplexity | クラスの Weighted Method Count (WMC) は、このクラスを修正・維持するためにどれだけの時間と労力が必要かを示す良い指標となります。WMC メトリクスは、クラスで宣言されているすべてのメソッドの複雑さの合計として定義されます。メソッドの数が多いということは、このクラスが派生クラスに対してより大きな影響を与える可能性があることも意味します。 |


### controversial ルール説明

| オプション | 説明 |
|:-|:-|
| Superglobals | スーパーグローバル変数に直接アクセスすることは、バッドプラクティスであると考えられています。これらの変数は、例えばフレームワークで提供されるオブジェクトにカプセル化されるべきです。 |
| CamelCaseClassName | クラス名にはCamelCase記法を使用するのがベストプラクティスとされています。 |
| CamelCasePropertyName | 属性の命名にはキャメルケース表記を使用するのがベストプラクティスとされています。 |
| CamelCaseMethodName | メソッドの命名にはキャメルケース表記を用いるのがベストプラクティスとされています。 |
| CamelCaseParameterName | パラメータ名にはキャメルケース表記を使用するのがベストプラクティスとされています。 |
| CamelCaseVariableName | 変数名にはキャメルケース表記を使用するのがベストプラクティスとされています。 |

### design ルール説明

| オプション | 説明 |
|:-|:-|
| ExitExpression | 通常のコード内の終了式はテスト不可能であるため、避けるべきである。終了式を何らかの起動スクリプトに移動して、エラー/例外コードを呼び出し環境に返すことを検討してください。 |
| EvalExpression | eval-expressionはテスト不可能であり、セキュリティリスクであり、バッドプラクティスです。したがって、避けるべきです。eval 式を通常のコードに置き換えることを検討してください。 |
| GotoStatement | Gotoはコードを読みにくくし、この言語構造を使用するアプリケーションの制御フローを理解することはほぼ不可能です。したがって、これは避けるべきです。Gotoを、より読みやすい通常の制御構造や分離したメソッド/関数に置き換えることを検討してください。 |
| NumberOfChildren | 子クラスの数が多すぎるクラスは、クラス階層のバランスが悪いことを示す指標です。このクラス階層をリファクタリングすることを検討する必要があります。 |
| DepthOfInheritance | 依存関係が強すぎるクラスは、クラスのいくつかの品質面に悪影響を及ぼします。これには、安定性、保守性、理解しやすさなどの品質基準が含まれます。 |
| CouplingBetweenObjects | 依存関係が強すぎるクラスは、クラスのいくつかの品質面に悪影響を及ぼします。これには、安定性、保守性、理解しやすさなどの品質基準が含まれます。 |
| DevelopmentCodeFragment | ar_dump()、print_r()などの関数は通常、開発時にのみ使用されるため、実運用コードでこのような呼び出しがあるのは、単に忘れられただけという良い指標になります。 |
| EmptyCatchBlock | 通常、空の try-catch は悪い考えです。なぜなら、エラー条件を黙って飲み込み、実行を続行するからです。時にはこれが正しい場合もありますが、多くの場合、開発者が例外を発見し、それに対して何をすべきかがわからず、問題を解決するために空のキャッチを使用したことの表れなのです。 |
| CountInLoopExpression | ループの式で count/sizeof を使用することはバッドプラクティスであり、特にループが配列を操作する場合、count が反復するたびに発生するため、多くのバグの原因となる可能性があります。 |

### naming ルール説明

| オプション | 説明 |
|:-|:-|
| LongClassName | クラスやインターフェースが過度に長い名前で宣言されている場合に検出します。 |
| ShortClassName | クラスやインターフェースの名前が非常に短い場合に検出されます。 |
| ShortVariable | フィールド、ローカル、パラメーターが非常に短い名前である場合に検出します。 |
| LongVariable | フィールド、形式変数、ローカル変数が長い名前で宣言されている場合に検出されます。 |
| ShortMethodName | 非常に短いメソッド名が使用されている場合に検出されます。 |
| ConstructorWithNameAsEnclosingClass | コンストラクタのメソッド名は、クラス名と同じであってはなりません。 |
| ConstantNamingConventions | クラス/インターフェースの定数名は、常に大文字で定義する必要があります。 |
| BooleanGetMethodName | getX()' という名前のメソッドで、戻り値の型が 'boolean' であるものを探します。慣習として、これらのメソッドには 'isX()' あるいは 'hasX()' という名前をつけることになっています。 |

### unusedcode ルール説明

| オプション | 説明 |
|:-|:-|
| UnusedPrivateField | プライベートフィールドが宣言され、かつ/または値が割り当てられているが、使用されていない場合を検出する。 |
| UnusedLocalVariable | ローカル変数が宣言または割り当てられたが、使用されていないことを検出します。 |
| UnusedPrivateMethod | 未使用のプライベートメソッド：プライベートメソッドが宣言されているが、使用されていない場合に検出されます。 |
| UnusedFormalParameter | メソッドやコンストラクタにパラメータを渡して、そのパラメータを使用しないことは避けてください。 |

# カスタムルールセット

上記の中で使いたいルールだけを組み込んで実行していくのが正しい使い方っぽい。
下記ディレクトリにデフォルトのルールセットが格納されています

```bash
# ls vendor/phpmd/phpmd/src/main/resources/rulesets/

cleancode.xml
codesize.xml
controversial.xml
design.xml
naming.xml
unusedcode.xml
```

デフォルトファイルをどこか適当なプロジェクトディレクトリ内に複製して、使用したくないルールは削除した上でオプションに指定していく.

```bash
# ディレクトリ作成
mkdir phpmd/
# copy
cp vendor/phpmd/phpmd/src/main/resources/rulesets/* phpmd/

# example
./vendor/bin/phpmd app/Http/Controllers/TopController.php text phpmd/creancode.xml,phpmd/codesize.xml
```