セキュリティ

PHPMDとは?PHPコードの静的解析ツールの使い方・ルールセット・警告抑制まで【2.15対応】

PHPMD(PHP Mess Detector)は、PHPのソースコードを実行せずに解析し、複雑すぎるメソッドや未使用の変数、深すぎるネストといった「保守性を下げる書き方」を検出する静的解析ツールです。コーディングスタイルではなくコードの構造を評価する点が特徴で、CI/CDに組み込めばコード品質のばらつきを自動で止められます。この記事では、インストールから6つのルールセット、phpmd.xmlによるカスタマイズ、特定の警告だけを無視する方法、GitHub Actionsでの自動化までを実際のコマンドで解説します。バージョンは安定版の2.15.0(PHP 5.3.9以上で動作)を前提とします。

まとめ:PHPMDの要点

  • 役割:PHPMDはコードの構造(複雑度・未使用コード・命名・サイズ)を検査する静的解析ツール。スタイル検査のPHP_CodeSniffer、型検査のPHPStanとは守備範囲が違い、併用が前提。
  • 導入composer require --dev phpmd/phpmd でプロジェクトに追加し、vendor/bin/phpmd で実行する。
  • 実行phpmd <対象> <出力形式> <ルールセット> の3引数が基本形。例:phpmd src text cleancode,codesize
  • ルールセット:cleancode / codesize / controversial / design / naming / unusedcode の6種を組み合わせる。
  • 誤検知対策:ルールを丸ごと切るより、@SuppressWarnings でピンポイント抑制、既存プロジェクトはベースライン(--generate-baseline)で既存違反を凍結するのが実務的。

PHPMDとは何か:PHP Mess Detectorの役割

PHPMDは「PHP Mess Detector」の略で、公式サイトとGitHubの phpmd/phpmd リポジトリで開発が続くオープンソースのツールです。もともとはコードメトリクスを計測する PHP Depend の派生プロジェクトで、Javaの静的解析ツール PMD のPHP版を目指して作られました。コードを実行せずにソースを読み解いて問題を指摘するため、テストを回さなくても「危ない書き方」を早い段階で洗い出せます。

PHPMDが見つけるのは、たとえば次のような構造上の問題です。

  • 循環的複雑度が高すぎるメソッド(分岐が多く、テストも読解も難しい)
  • 長すぎる関数・クラス、多すぎる引数やプロパティ
  • 未使用のローカル変数・privateメソッド・引数
  • 短すぎる/長すぎる変数名などの命名の問題

スタイル(インデントや空白)の指摘ではなく、こうした「後で保守コストになる構造」を突く点がPHPMDの価値です。安定版は2.15.0で、動作要件はPHP 5.3.9以上ですが、実運用ではサポート中のPHP 8系で使うのが無難です。

PHPMDとPHPStan・PHP_CodeSnifferの違い

PHPの静的解析ツールは役割が重ならないため、「どれか一つ」ではなく組み合わせて使います。混同しやすい3ツールの守備範囲を整理します。

ツール 検査対象 見つけるもの
PHPMD コードの構造・複雑さ 複雑すぎるメソッド、未使用コード、命名、サイズ
PHPStan 型・実行前のロジック 型の不整合、存在しないメソッド呼び出し、nullアクセス
PHP_CodeSniffer コーディング規約・スタイル PSR-12違反、インデント、命名規約の逸脱

PHPMDは「保守しにくい構造」を、PHPStanは「動かすと壊れる型の問題」を担当します。型の検出まで踏み込みたい場合や、より高速なツールを検討したいなら、PsalmとPHPStanの違いを比較した記事や、Rust製で高速なMago静的解析ツールの解説もあわせて検討してください。PHPMD+PHPStan+PHP_CodeSnifferの3点セットが、PHPプロジェクトの品質ゲートの定番構成です。

PHPMDのインストール手順(Composer)

インストールはComposer経由が標準です。開発時にしか使わないため、プロジェクトには開発依存(--dev)として入れます。

composer require --dev phpmd/phpmd

これで vendor/bin/phpmd に実行ファイルが入ります。複数プロジェクトで共通に使いたい場合はグローバルインストールも可能です。

composer global require phpmd/phpmd

導入できたらバージョンを確認します。値が表示されればインストール成功です。

vendor/bin/phpmd --version

Composerを使わず単体で配布されているPHAR版を phpmd.phar として置いて実行する方法もありますが、依存関係をプロジェクトで固定できるComposer導入をおすすめします。

PHPMDの基本的な使い方とコマンド構文

PHPMDのコマンドは、解析対象・出力形式・ルールセットの3つを順に渡すのが基本形です。

phpmd <対象パス> <出力形式> <ルールセット>

たとえば src ディレクトリを cleancode ルールで解析し、結果をテキスト表示するには次のようにします。

phpmd src text cleancode

ルールセットはカンマ区切りで複数指定できます。対象パスも複数ディレクトリをカンマでつなげます。

phpmd src,app text cleancode,codesize,unusedcode

出力形式は text のほか xml / json / html / github などを選べます。CI/CDで結果を機械処理するなら xmljson、GitHub上に注釈として出したいなら github が便利です。

phpmd src xml phpmd.xml --reportfile phpmd-report.xml

よく使うコマンドラインオプション

オプション 役割
–reportfile <path> 結果を標準出力ではなくファイルへ書き出す
–minimumpriority <1-5> 指定した優先度以上の違反だけを表示する
–exclude <pattern> 解析から除外するパスを指定する
–suffixes <ext> 対象とする拡張子を指定する(既定は php)
–strict @SuppressWarnings で抑制した違反も含めて報告する
–ignore-violations-on-exit 違反があっても終了コードを0にする(CIを止めない)

CI/CDでは違反があるとPHPMDが非ゼロの終了コードを返し、そのままだとビルドが失敗します。まず検出だけ始めたい導入初期は --ignore-violations-on-exit でビルドを止めずに可視化し、運用が固まってから外して品質ゲートにする、という段階導入が現実的です。

PHPMDの6つのルールセット

PHPMDには標準で6つのルールセットが同梱されています。プロジェクトの性質に合わせて必要なものだけを組み合わせます。

ルールセット 目的 代表的なルール
cleancode クリーンな設計の徹底 ElseExpression、BooleanArgumentFlag、StaticAccess
codesize 複雑さ・サイズの抑制 CyclomaticComplexity、ExcessiveMethodLength、TooManyMethods
controversial 賛否が分かれる規約 CamelCase系の命名、Superglobals
design 設計上の危険な書き方 ExitExpression、EvalExpression、CouplingBetweenObjects
naming 命名の適切さ ShortVariable、LongVariable、ShortMethodName
unusedcode 使われていないコード UnusedLocalVariable、UnusedPrivateMethod

まず入れるなら codesizeunusedcode が費用対効果が高く、指摘の意味も分かりやすい2つです。codesize の中心である サイクロマティック複雑度は「分岐の多さ=バグの入りやすさ・テストの書きにくさ」の指標で、しきい値超えのメソッドは分割の候補になります。一方 controversial はチームの命名文化に強く依存するため、いきなり全部を有効にすると警告が溢れがちです。命名の基準を先に決めたい場合は、命名規則の整理を基準にして有効化するルールを取捨選択します。

phpmd.xmlによるルールのカスタマイズ

コマンドラインでルールセット名を渡す代わりに、phpmd.xml という設定ファイルを用意すると、有効にするルール・しきい値・除外パスをプロジェクトで固定できます。GitでバージョンしていけばチームとCIで同じ基準を共有できます。

<?xml version="1.0"?>
<ruleset name="My Rules">
    <description>プロジェクト用のPHPMDルール</description>
    <rule ref="rulesets/unusedcode.xml" />
    <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
        <properties>
            <property name="reportLevel" value="8" />
        </properties>
    </rule>
    <exclude-pattern>*/tests/*</exclude-pattern>
</ruleset>

この例では unusedcode をまるごと有効にしつつ、循環的複雑度の警告しきい値を 8 に緩め、tests ディレクトリを解析対象から外しています。作成した設定ファイルは、ルールセット名の位置に渡すだけで適用されます。

phpmd src text phpmd.xml

デフォルトのしきい値が自分たちのコードに厳しすぎると感じたら、ルールを無効化する前に property で数値を調整するのが定石です。ルールごと消すと、本来拾いたい重大な違反まで見逃すことになります。

特定の警告だけを無視する方法とベースライン運用

PHPMDを既存のコードベースに入れると、初回は大量の警告が出ます。ここでルールを片っ端からオフにすると検査の意味が薄れるため、「どこを、どの粒度で無視するか」を使い分けるのが実務のコツです。

1行・1メソッド単位で無視するなら、対象のクラスやメソッドのdocコメントに @SuppressWarnings を書きます。意図的に残している未使用引数などを黙らせるのに使います。

/**
 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
 */
public function handle($request, $unused)
{
    return $request;
}

@SuppressWarnings(PHPMD) のようにルール名を省くとそのブロック全体の警告を抑制できますが、範囲が広すぎるので原則はルール名まで指定します。抑制した箇所も確認したいときは --strict を付けて実行すると、@SuppressWarnings を無視して全件報告します。

既存の違反をまとめて棚上げしたい場合は、ベースライン機能(2.10.0以降)が有効です。次のコマンドで、現時点の違反を phpmd.baseline.xml に記録します。

phpmd src text phpmd.xml --generate-baseline

以降の解析ではベースラインに載った違反は報告されず、新しく増えた違反だけが検出されます。レガシーコードを後からPHPMDで守る際、「既存の負債は凍結し、今日以降のコードは劣化させない」という運用がこれで実現できます。既に直した違反をベースラインから掃除するには --update-baseline を使います。ルールを丸ごと切るのは最後の手段にして、まずは @SuppressWarnings とベースラインで面を絞り込むのが、警告疲れを起こさず品質を上げるやり方です。

GitHub ActionsへのPHPMD組み込み(CI/CD)

PHPMDはプルリクエストやプッシュのたびに自動実行してこそ効果が出ます。GitHub Actionsに組み込む例を示します。

name: PHPMD
on: [push, pull_request]
jobs:
  phpmd:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          tools: phpmd
      - name: Run PHPMD
        run: phpmd src github phpmd.xml

出力形式に github を指定すると、違反がプルリクエストの該当行に注釈として表示され、レビュー時に気づきやすくなります。導入直後にすべてのビルドを落とすとチームの反発を招くため、初期は前述の --ignore-violations-on-exit で警告の可視化から始め、合意が取れたら品質ゲートに昇格させます。ローカルでもコミット前に検査したい場合は、gitのpre-commitフックから phpmd を呼び出す構成が使えます。

よくある質問(FAQ)

PHPMDとPHPStanはどちらを使うべきですか?

どちらか一方ではなく併用が基本です。PHPMDはコード構造の複雑さや未使用コードを、PHPStanは型の不整合や実行前に分かるバグを検出します。役割が重ならないため、両方を通すことでカバー範囲が広がります。

PHPMDは無料で使えますか?

はい。PHPMDはオープンソースで、商用プロジェクトでも無料で利用できます。Composerでインストールし、追加のライセンス費用はかかりません。

特定の警告だけを無視するにはどうすればよいですか?

対象のメソッドやクラスのdocコメントに @SuppressWarnings(PHPMD.ルール名) を書くと、その箇所の該当ルールだけを抑制できます。プロジェクト全体で不要なルールは phpmd.xml から外し、既存の違反をまとめて棚上げするならベースライン(--generate-baseline)を使います。

phpmd.xmlはどこに置けばよいですか?

プロジェクトのルート直下に置き、Gitで管理するのが一般的です。実行時にルールセット名の位置へファイルパスを渡せば適用されます。CIとローカルで同じファイルを参照させることで、検査基準を統一できます。

PHPMDが動かない・エラーが出るときは?

まず phpmd --version でインストールとPATHを確認します。動作要件はPHP 5.3.9以上ですが、古いPHPや大規模コードでメモリ不足になる場合は、php -d memory_limit=512M vendor/bin/phpmd ... のようにメモリ上限を上げて実行します。ルールセット名や phpmd.xml のパス指定ミスもよくある原因です。

関連記事

資料請求

RELATED POSTS 関連記事