PHPMD(PHP Mess Detector) を試してみた

ZendStudioに同梱されている「ZendCodeAnalyzer」というツールを業務で使ってたんですが,
残念ながらZend Studio 7 評価版で確認したところ含まれていないようです。*1
※ZendCodeAnalyzerを使用するにはZendStudioのライセンスが必要です。


そこで代わりになるものを探してたらありました,PHP Mess Detector です。
PHPMDとは,JavaでいうところのPMDで,以下のような問題を見つけてくれます。

  • バグの可能性
  • 準最適なコード
  • 複雑な式
  • 未使用パラメータ,メソッド,プロパティ


今日(2010/05/26)時点の最新版が,PHPMD 0.2.5 (2010/04/03 released) なので,早速このバージョンで試してみます。

前提環境

  • PHP >= 5.2.3
  • PHP_Depend >= 0.9.11
[root@localhost ~]# pear channel-discover pear.pdepend.org
[root@localhost ~]# pear install pdepend/PHP_Depend-beta

PHPMDのインストール

[root@localhost ~]# pear channel-discover pear.phpmd.org
[root@localhost ~]# pear channel-discover pear.pdepend.org
[root@localhost ~]# pear install --alldeps phpmd/PHP_PMD-alpha
[root@localhost ~]# phpmd
Mandatory arguments:
1) A php source code filename or directory
2) A report format
3) A ruleset filename or a comma-separated string of rulesetfilenames

Optional arguments that may be put after the mandatory arguments:
--minimumpriority: rule priority threshold; rules with lower priority than this will not be used
--reportfile: send report output to a file; default to STDOUT
--extensions: comma-separated string of valid source code filename extensions
--ignore: comma-separated string of patterns that are used to ignore directories

テスト用のphpファイルを作成

<?php
class Something
{
    private static $FOO = 2; // unused
    private $i = 5; // unused
    private $j = 6;
    public function addOne()
    {
        return $this->j++;
    }

    private function foo() {} // unused

    private function bar()
    {
        $i = 5; // unused
        $j = 5;

        switch ($j) {
            case 0:
                if (1) {
                    // hoge
                }
                break;
            case 1: /* hoge */ break;
            case 2: /* hoge */ break;
            case 3: /* hoge */ break;
            case 4: /* hoge */ break;
            case 5: /* hoge */ break;
            case 6: /* hoge */ break;
            case 7: /* hoge */ break;
            case 8: /* hoge */ break;
            case 9: /* hoge */ break;
            default: break;
        }
    }

    private function baz($hoge)
    {
        // $hoge is not used
    }
}

レポート:'text',ルールセット:'Code Size Rules' で実行

[user@localhost ~]$ phpmd test.php text codesize

test.php:14 The method bar() has a Cyclomatic Complexity of 12.

レポート:'text',ルールセット:'Naming Rules' で実行

[user@localhost ~]$ phpmd test.php text naming

test.php:5  Avoid variables with short names like $i
test.php:6  Avoid variables with short names like $j
test.php:16 Avoid variables with short names like $i
test.php:17 Avoid variables with short names like $j

レポート:'text',ルールセット:'Unused Code Rules' で実行

[user@localhost ~]$ phpmd test.php text unusedcode

test.php:4  Avoid unused private fields such as '$FOO'.
test.php:5  Avoid unused private fields such as '$i'.
test.php:12 Avoid unused private methods such as 'foo'.
test.php:14 Avoid unused private methods such as 'bar'.
test.php:16 Avoid unused local variables such as '$i'.
test.php:38 Avoid unused private methods such as 'baz'.
test.php:38 Avoid unused parameters such as '$hoge'.

レポート:'xml',ルールセット:'Unused Code Rules' で実行

[user@localhost ~]$ phpmd test.php xml unusedcode
<?xml version="1.0" encoding="UTF-8" ?>
<pmd version="0.2.5" timestamp="2010-04-25T10:49:34+09:00">
  <file name="test.php">
    <violation beginline="4" endline="4" rule="UnusedPrivateField" ruleset="Unused Code Rules" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedprivatefield" priority="3">
      Avoid unused private fields such as '$FOO'.
    </violation>
    <violation beginline="5" endline="5" rule="UnusedPrivateField" ruleset="Unused Code Rules" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedprivatefield" priority="3">
      Avoid unused private fields such as '$i'.
    </violation>
    <violation beginline="12" endline="12" rule="UnusedPrivateMethod" ruleset="Unused Code Rules" package="+global" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedprivatemethod" class="Something" method="foo" priority="3">
      Avoid unused private methods such as 'foo'.
    </violation>
    <violation beginline="14" endline="36" rule="UnusedPrivateMethod" ruleset="Unused Code Rules" package="+global" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedprivatemethod" class="Something" method="bar" priority="3">
      Avoid unused private methods such as 'bar'.
    </violation>
    <violation beginline="16" endline="16" rule="UnusedLocalVariable" ruleset="Unused Code Rules" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedlocalvariable" priority="3">
      Avoid unused local variables such as '$i'.
    </violation>
    <violation beginline="38" endline="41" rule="UnusedPrivateMethod" ruleset="Unused Code Rules" package="+global" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedprivatemethod" class="Something" method="baz" priority="3">
      Avoid unused private methods such as 'baz'.
    </violation>
    <violation beginline="38" endline="38" rule="UnusedFormalParameter" ruleset="Unused Code Rules" externalInfoUrl="http://phpmd.org/rules/unusedcode.html#unusedformalparameter" priority="3">
      Avoid unused parameters such as '$hoge'.
    </violation>
  </file>
</pmd>

良い感じの結果が出力されました:-)
ルールセットの種類およびチェック詳細は,http://phpmd.org/rules/index.html を参照してみてください。

*1:サポートに問い合わせたところ,機能を個別に利用する方法は開示出来ないとのことなので推測です。