kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

PHPMDのルールセットの作り方

phpmdのrulesetはxmlファイルとしてまとめておくことができます。
このファイルの内容を毎回調べてるので自分用メモ
公式ドキュメントに書いてあることがほとんどです。

https://phpmd.org/documentation/creating-a-ruleset.html

初期状態

雛形です。
nameとdescriptionは、それぞれ自分のプロジェクトにあったものにします。
descriptionはなくても大丈夫です。
ファイルの配置場所と名前はなんでも大丈夫ですが、
私は、大抵の場合、phpcsと一緒に使うので、phpmd.xmlというファイル名にすることが多いです。
配置場所は、プロジェクトのルートディレクトリにおいてしまうことが多いです。

<?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>
</ruleset>

ルールセットを追加してみる

phpmdが元々持っているunused code のrulesetを追加してみます。
使ってない変数とかを警告してくれるものなので、どんなプロジェクトでも入れるでしょう。
下記の書き方だと、 unused codeのすべてのruleを追加します。

unusedcodeの部分はドキュメントを見ても、
具体的にどんなものを指定すればよいか書いてないのですが、

https://phpmd.org/rules/index.html

のCurrent Rulesetsに書いてあるリストを、全て小文字にして、Rulesを消したもので大丈夫です。
具体的には、 Unused Code Rules: だったら、unusedcodeになります。

関係ないですが、下記ではdesctiprionを消してます。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         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">
    <!-- unused code の rule set を追加 -->
    <rule ref="rulesets/unusedcode.xml"/>
</ruleset>

ルールセットの中から一つだけルール追加してみる

さっきは、phpmdが用意してくれているルールセットの中から一つを選んで、
そのルールセットが持っているすべてのルールを追加しました。
今度は、ルールを一つだけ追加してみます。

Controversial Rulesというルールセットだと、
メソッド名などにキャメルケースを強制させるルールがあります。
しかし、プロジェクトによっては、スネークケースにしているところもあるでしょう。
その場合でも、Superglobals のルールだけは適用したいという場合の書き方です。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         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">
    <!-- unused code の rule set を追加 -->
    <rule ref="rulesets/unusedcode.xml"/>
    <!-- スネークケースのプロジェクトなので、Superglobalsだけ適用 -->
    <rule ref="rulesets/controversial.xml/Superglobals"/>
</ruleset>

ちなみに、1つのルールだけを除外したい場合は、excludeを使います。
例えば、Controversial Rulesというルールセットから、
CamelCaseClassName だけを除外したい場合は
下記のように書きます。

<rule ref="rulesets/controversial.xml">
    <exclude name="CamelCaseClassName"/>
</rule>

なので、前述のSuperglobalsだけ適用する場合は、

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

このように書き換えても同様です。
適用ルールが多いか、除外ルールが多いかで決めればいいと思います。

<rule ref="rulesets/controversial.xml">
    <exclude name="CamelCaseMethodName"/>
    <exclude name="CamelCaseParameterName"/>
    <exclude name="CamelCaseVariableName"/>
    <exclude name="CamelCasePropertyName"/>
    <exclude name="CamelCaseClassName"/>
</rule>

ルールのプロパティを変えたい場合

Naming Rules を適用すると、$id というよく使う変数名まで警告されてしまいます。
ShortVariable ルールのデフォルトのminimumが3だからです。
2文字以下の変数名は警告されます。
なので、$id だけは、警告から除外されるようにします。

ポイントは、一旦ShortVariableを除外することです。
除外しないと、元々の値が適用されてしまいます。
プロパティ名やどんな値を指定するかは、ドキュメントに書いてあります。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         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">
    <!-- unused code の rule set を追加 -->
    <rule ref="rulesets/unusedcode.xml"/>
    <!-- スネークケースのプロジェクトなので、Superglobalsだけ適用 -->
    <rule ref="rulesets/controversial.xml/Superglobals"/>
    <!-- id という変数名だけは許可 -->
    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable"/>
    </rule>
    <rule ref="rulesets/naming.xml/ShortVariable">
        <properties>
            <property name="exceptions" value="id"/>
        </properties>
    </rule>
</ruleset>

汎用サンプル

コードスタイルにPSRを適用していて、
phpunitで、テストコード書いているようなプロジェクトなら、
全部盛りで下記のような感じになると思います。

TooManyMethods,TooManyPublicMethodsで、
メソッド名がtestから始まる場合は、カウントから除外するようにしています。
テストクラスは、メソッドが増えること多いので。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         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">
    <rule ref="rulesets/unusedcode.xml"/>
    <rule ref="rulesets/design.xml"/>
    <rule ref="rulesets/controversial.xml"/>
    <!-- id という変数名だけは許可 -->
    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable"/>
    </rule>
    <rule ref="rulesets/naming.xml/ShortVariable">
        <properties>
            <property name="exceptions" value="id"/>
        </properties>
    </rule>
    <!-- テストのメソッドはカウントから除外 -->
    <rule ref="rulesets/codesize.xml">
        <exclude name="TooManyMethods"/>
        <exclude name="TooManyPublicMethods"/>
    </rule>
    <rule ref="rulesets/codesize.xml/TooManyMethods">
        <properties>
            <property name="ignorepattern" value="(^(set|get|test))i"/>
        </properties>
    </rule>
    <rule ref="rulesets/codesize.xml/TooManyPublicMethods">
        <properties>
            <property name="ignorepattern" value="(^(set|get|test))i"/>
        </properties>
    </rule>
</ruleset>