PHPコードの可読性を計測する指標、ローカル変数酷使度

読み難いコードに悩んでませんか?

常日頃、目にするPHPコードが読みずらいと感じたことはあるでしょうか?
ソフトウェアを健全に保守していくためには、読み易いコードの方が良いに決まってます。
ベテラン技術者のセンスとかではなく、コードの複雑さの指標があると、論理的に改善できます。
コードの複雑さを数値化する指標は、コードの分岐の深さに注目した循環的複雑度という指標があります。
過去に、PhpMatics等のツールを使って循環的複雑度を参考に改善を行なおうとしましたが、かなり大掛りな構造変更が必要になってしまう傾向があると感じました。
もっと簡単に使える指標があると良いと思いました。

ローカル変数が酷使されていると読み難いコードになるという仮説

ローカル変数のスコープが広がってしまっていると、気にしなければいけない範囲が増えてしまいます。
ローカル変数を更新していると状態を持つため、気にしなければいけない内容が増えてしまいます。
独自用語ですが、ローカル変数のスコープが広かったり、参照箇所が多かったり、状態を持つ変数だったりすると数値が高くなる、「ローカル変数酷使度(Local Variable Hard Usage)」という指標を定義してみました。

ローカル変数酷使度(Local Variable Hard Usage)

変数のスコープの広さと更新頻度を測定し、数値化することで、問題のあるコードを客観的に判断できるようにしようと考えました。
ここ数日、PoC(概念実証)のため、GitHub Copilotと一緒に「php-variable-hard-usage」というCLIツールを作成していました。
php-variable-hard-usage は、PHPのスコープである、関数とクラスのメソッドのスコープ範囲で、参照されている変数の出現行番号のばらけ具合を数値化して合計してローカル変数酷使度としています。
もうすこし具体的に言うと、任意のスコープ内で同一変数の参照行数の平均を求めて、それぞれの行数からの平均との差を合計したものを、変数の酷使度(variable hard usage)として求めています。

php-variable-hard-usage の使い方

インストール

composerでインストールします。
composer require --dev smeghead/php-variable-hard-usage

使い方

第一引数に、計測したいPHPファイルのパスを指定して実行すると、最大と平均のローカル変数酷使度と、各スコープの結果がJSON形式で表示されます。
vendor/bin/php-variable-hard-usage somewhere/your-php-file.php
{
    "maxVariableHardUsage": 51,
    "avarageVariableHardUsage": 14.75,
    "scopes": [
        {
            "name": "VariableAnalyzer::__construct",
            "variableHardUsage": 2
        },
        {
            "name": "VariableAnalyzer::analyze",
            "variableHardUsage": 0
        },
        {
            "name": "VariableAnalyzer::analyzeFunction",
            "variableHardUsage": 51
        },
        {
            "name": "VariableAnalyzer::calcVariableHardUsage",
            "variableHardUsage": 6
        }
    ]
}

実際の改善例

以下のコード例は、ローカル変数の使い方の例として挙げているものなので、簡易的なコードとなっています。(ゼロ除算の対策がされていないなどなどのエラー処理が不足しています)

悪いコード

<?php

/**
 * 最小値と最大値を除いて平均を返却します。
 */
function ugly_my_average(array $items): float {
    $minValue = PHP_INT_MAX;
    $maxValue = PHP_INT_MIN;
    
    for ($i = 0; $i < count($items); $i++) {
        $v = $items[$i];
        if ($v < $minValue) { $minValue = $v; } if ($v > $maxValue) {
            $maxValue = $v;
        }
    }
    $sum = 0;
    for ($i = 0; $i < count($items); $i++) {
        $v = $items[$i];
        if (in_array($v, [$minValue, $maxValue])) {
            continue;
        }
        $sum += $v;
    }
    return $sum / (count($items) - 2);
}

$items = [3, 99, 40, 45, 50, 52];
print_r(ugly_my_average($items));

改善コード

<?php

/**
 * 最小値と最大値を除いて平均を返却します。
 */
function improved_my_average(array $items): float {
    sort($items);
    $newItems = array_slice($items, 1, count($items) - 2);
    $sum = array_sum($newItems);
    return $sum / count($newItems);
}

$items = [3, 99, 40, 45, 50, 52];
print_r(improved_my_average($items));

計測

上の2つの例のコードを、php-variable-hard-usage で計測してみましょう。
# vendor/bin/php-variable-hard-usage test/fixtures/ugly_my_average.php 
{php -r "unlink('composer-setup.php');"
    "maxVariableHardUsage": 155,
    "avarageVariableHardUsage": 155,
    "scopes": [
        {
            "name": "ugly_my_sum",
            "variableHardUsage": 155
        }
    ]
}
# vendor/bin/php-variable-hard-usage test/fixtures/improved_my_average.php 
{
    "maxVariableHardUsage": 6,
    "avarageVariableHardUsage": 6,
    "scopes": [
        {
            "name": "improved_my_sum",
            "variableHardUsage": 6
        }
    ]
}
改善により、ローカル変数酷使度が、155 から 6 に下がっているのが解ります。
レビュー等の際に、修正前と修正後で計測することで、ローカル変数の使い方が改善具合を確認するのに使えるかもしれません。
注意事項としては、どんな指標にも言えることだと思いますが、ローカル変数酷使度をハックしようとすると可読性が落ちたり変更しにくくなることもあります。

まとめ・今後の展望

「php-variable-hard-usage」を使うことで、PHPのコードの改善を簡易的に可視化することができます。
今後は、変数の更新頻度についての情報を含めた解析も行なえるようにしたいです。
ぜひ試してフィードバックをおねがいします。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

This site uses Akismet to reduce spam. Learn how your comment data is processed.