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

PHPプログラムの複雑度をPHPMDで客観的に測定する

【スポンサーリンク】

プログラムが複雑になると、テストケースが増大し、保守性が低くなることは直感的・経験的に理解できますが、この複雑度を客観的に測定する方法として循環的複雑度(Cyclomatic complexity)があります。PHPでは、PHPMDを使って循環的複雑度を測定することができます。

PHPMD - PHP Mess Detector

インストール

PHPMDをインストールする方法はいくつかありますが、ここでは簡単な phpmd.phar を利用する方法を紹介します。 phpmd.phar を利用するためには、最新版をダウンロードして実行権限を付与するだけです。

$ wget -c http://static.phpmd.org/php/latest/phpmd.phar -O phpmd
$ chmod +x phpmd

なお、 phpmd.phar を実行するためには php-bz2 が必要なので、インストールされていない場合はインストールしておきます。次に示すのは Ubuntu 16.04 で apt を使ってインストール例です。

$ sudo apt install php-bz2

以下を実行してPHPMDのバージョン情報が表示されることを確認します。

$ ./phpmd --version
PHPMD 2.5.0

複雑度の基準

それでは早速PHPMDで循環的複雑度を測ってみますが、その前に複雑度の基準について説明します。PHPMDのドキュメントによると、複雑度は以下のように分類されます。

数値 複雑度
1~4
5~7
8~10
11以上 非常に高い

https://phpmd.org/rules/codesize.html#cyclomaticcomplexity

デフォルトではPHPMDがレポートを出力する閾値は10となっているため、複雑度がそれより低い場合はレポートを出力しません。今回は確認のため複雑度が低くてもレポートを出力するようにPHPMDのルールセットを変更します。ルールセットを変更するには以下のようなXMLruleset.xml というファイル名で保存します。reportLevel を 1 に設定するのがポイントです。

<?xml version="1.0"?>
<ruleset
    xsi:nonamespaceschemalocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
    xsi:schemalocation="http://pmd.sf.net/ruleset/1.0.0
                        http://pmd.sf.net/ruleset_xml_schema.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://pmd.sf.net/ruleset/1.0.0"
    name="PHPMD rule set">
    <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
        <properties>
            <property value="1" name="reportLevel"></property>
        </properties>
    </rule>
</ruleset>

プログラム例

それでは簡単なプログラムを書いてPHPMDを実行してみます。まずは次のようなプログラムを sample1.php というファイル名で保存します。

<?php
function sample1()
{
    echo "Hello, world.";
}

次のコマンドを実行します。

$ ./phpmd sample1.php text ruleset.xml
sample1.php:2  The function sample1() has a Cyclomatic Complexity of 1. The configured cyclomatic complexity threshold is 1.

Cyclomatic Complexity of 1 が出力されたので、循環的複雑度は 1 であることが確認できます。

次に、プログラムを少し複雑にしてみます。次のようなプログラムを sample2.php というファイル名で保存します。

<?php
function sample2($format)
{
    if ($format === 'xml' || $format === 'text' || $format === 'html') {
        echo "valid format.";
    } else {
        echo "invalid format.";
    }
}
$ ./phpmd sample2.php text ruleset.xml
sample2.php:2 The function sample2() has a Cyclomatic Complexity of 4. The configured cyclomatic complexity threshold is 1.

Cyclomatic Complexity of 4 が出力されたので、循環的複雑度は 4 であることが確認できます。

このプログラムを in_array() を使うように変更してみます。

<?php
function sample2($format)
{
    if (in_array($format, array('xml', 'text', 'html'), true)) {
        echo "valid format.";
    } else {
        echo "invalid format.";
    }
}
$ ./phpmd sample2.php text ruleset.xml
sample2.php:2 The function sample2() has a Cyclomatic Complexity of 2. The configured cyclomatic complexity threshold is 1.

循環的複雑度は 2 になりました。つまり、先ほどの in_array() を使わないバージョンより、 in_array() を使うバージョンの方が循環的複雑度が小さくなったことを意味します。

まとめ

循環的複雑度はあくまで指標のひとつです。循環的複雑度が小さいプログラムの可読性や効率が必ず高いとは限りません。しかしながら、プログラムの複雑度を計測することで、プログラムを変更した後の複雑度の変化を客観的に知ることができます。プログラミングの際に循環的複雑度を意識し、計測することで、よりよいソフトウェアを開発するためのヒントになるのではないでしょうか。

新装版 達人プログラマー 職人から名匠への道

新装版 達人プログラマー 職人から名匠への道

docs.hatenablog.jp