読者です 読者をやめる 読者になる 読者になる

恥知らずのウェブエンジニア -web engineer, shameless

これは一歩を踏み出すことができない者たちのブログ

phpmdでコード解析+チェックルールのカスタマイズしてみる

youngforever.hatenablog.com

前回のphpcsに続きコーディングの品質向上のためphpmdを使うようにしてみました。

PHPMD - PHP Mess Detector

phpmdは潜在的にバグになりそうなコードや改善の余地があるコードなどを検出してくれるツールです

phpmdインストール

公式通り、composer.jsonに下記を追記してcomposer installします

{
    "require-dev": {
        "phpmd/phpmd" : "*"
    }
}

使い方

コマンドラインで実行する際は下記のように実行できます。

phpmd /path/to/file xml codesize,naming,cleancode

xml以後のオプションで検出ルールを指定します。 ルールが設定してあるxmlvendor/phpmd/phpmd/src/main/resources/rulesets/以下にあります。

デフォルトでは下記があります。

またそれぞれのルールの実装はvendor/phpmd/phpmd/src/main/php/PHPMD/Rule/にあります。

なので、ルールのカスタマイズするには、新規にruleを定義したxmlを作成して、それに対するチェックの実装を行っていく感じになります。

カスタマイズルールを作ってみる

今回はtypoチェックを実装してみました。

まずカスタマイズルール用にxmlを作ります。

vi vendor/phpmd/phpmd/src/main/resources/rulesets/myStandard.xml


<?xml version="1.0"?>

<ruleset name="My Standard Rules">

    <description>
This ruleset contains a my standard rules.
    </description>

    <rule name="CheckTypo"
          since="0.2"
          message = "Maybe typo. {0} : {1} -> {2}"
          class="PHPMD\Rule\MyStandard\CheckTypo"
          externalInfoUrl="#">
        <description>
            <![CDATA[
Maybe typo.
            ]]>
        </description>
        <priority>1</priority>
        <properties />
        <example>
            <![CDATA[
protocal->protocol
recompence->recompense
            ]]>
        </example>
    </rule>
</ruleset>

上記のように定義して、PHPMD\Rule\MyStandard\CheckTypoにチェックルールの実装を行っていきます。

typo例はこちらのものを参考としました。

Wikipedia:Lists of common misspellings/For machines - Wikipedia, the free encyclopedia

既存の実装を元に下記のように実装してみました。 ./resources/typoList.csvtypo単語リストを置いています

vendor/phpmd/phpmd/src/main/php/PHPMD/Rule/MyStandard/CheckTypo.php


namespace PHPMD\Rule\MyStandard;

use PHPMD\AbstractNode;
use PHPMD\AbstractRule;
use PHPMD\Rule\FunctionAware;
use PHPMD\Rule\MethodAware;

/**
 * This rule CheckTypo.
 * typo check
 *
 */
class CheckTypo extends AbstractRule implements MethodAware, FunctionAware
{

    private static $typoList = [];

    /**
     * This method checks typo
     *
     * @param \PHPMD\AbstractNode $node
     * @return void
     */
    public function apply(AbstractNode $node)
    {
        $typoList = $this->getTypoList();

        foreach ($node->findChildrenOfType('Variable') as $variable) {
            $image = $variable->getImage();
            foreach ($typoList as $key => $value) {
                if (preg_match("/$key/", $image)) {
                    $this->addViolation($variable, array($image, $key, $value));
                    break;
                }
            }
        }
    }

    /**
     * get typoList
     * @return void
     */
    public function getTypoList()
    {
        if (self::$typoList) {
            return self::$typoList;
        }
        $typoList = [];
        $fp = fopen(dirname(__FILE__) . "/resources/typoList.csv", "r");
        while (!feof($fp)) {
                $tmpArr = fgetcsv($fp);
                $typoList[$tmpArr[0]] = $tmpArr[1];
        }
        self::$typoList = $typoList;
        fclose($fp);

        return self::$typoList;
    }
}

実行はこんな感じで、 phpmd /path/to/file xml myStandard

既存の実装を元になんとなくやってみましたが、変数名のtypo検出ができました!

こんな感じでカスタマイズルールを実装することでプロジェクト共通で品質向上やレビューの負荷軽減等していけたらと思いますん。