🍝

PHPのプログラムを改善しながらローカル変数酷使度を理解する

に公開

PHPのローカル変数酷使度を計測するツールを作ってます。この記事で記載されているローカル変数酷使度は、このツールで計測しました。

https://github.com/smeghead/php-variable-hard-usage

プログラムの可読性を向上させるためのテクニックは様々なものがあります。アーキテクチャ設計の観点からソースコードを整理する方法、ビジネスロジックを分かりやすく構築する手法、テストしやすいコードを書くための工夫——どれも重要であり、全体としてのプログラムの品質を高めるためには欠かせません。

しかし、この記事で取り上げるのは、そうした大局的な視点ではなく、「極端に局所的な可読性」に焦点を当てた話です。具体的には、「ローカル変数がどのように使われているか?」というミクロな視点から、コードの読みやすさを測るための指標として「ローカル変数酷使度」を紹介します。

この指標は万能ではありませんが、局所的な改善の道具として活用できます。

局所的な観点でプログラムの可読性や保守性を向上させるためには、ローカル変数の適切な管理が重要です。特に、「ローカル変数を酷使しすぎるとコードが複雑になり、可読性や修正のしやすさが低下する」 という問題が発生します。

本記事では、PHPでCSVの1行を生成する関数を例に取り、ローカル変数酷使度 を段階的に改善しながら、その有用性を示していきます。

ローカル変数酷使度とは?

ローカル変数酷使度 とは、関数内の変数のスコープの広さや更新頻度を数値化した独自の指標です。(https://blog.starbug1.com/archives/3022) 変数の使用範囲が広く、更新回数が多いほど値が大きくなり、コードの可読性が低下しやすくなります。

今回の例では、以下の関数を順番に改善しながら、ローカル変数酷使度を減らしていきます。では、実際のコードを見てみましょう。


改善のステップ

ここに、一つのプログラムがあります。このコードは、配列の値をカンマ区切りのCSV形式の文字列に変換する関数ですが、何やら読みづらく、手を加えたくなる部分がいくつかあります。

ステップ 1: 初期状態(ローカル変数酷使度: 131)

<?php

/** ローカル変数酷使度: 131 */
function createCsvLine1($values) {
    $result = "";
    $count = count($values);

    for ($i = 0; $i < $count; $i++) {
        $value = $values[$i];

        // 文字列なら " で囲む
        if (is_string($value)) {
            $formattedValue = '"' . $value . '"';
        } else {
            $formattedValue = $value;
        }

        if ($i == 0) {
            $result .= $formattedValue;
        } else {
            $result .= "," . $formattedValue;
        }
    }

    return $result;
}

変数 $result に直接文字列を連結しながら処理を進めていますが、$i というインデックス変数を使い、最初の要素だけ特別扱いしているため、コードの見通しが悪くなっています。

ステップ 2: foreach に置き換え(ローカル変数酷使度: 106)

「配列の全要素を処理するのなら、for よりも foreach の方が直感的では?」 そう考えて、コードを修正しました。

<?php

/** ローカル変数酷使度: 106 */
function createCsvLine2($values) {
    $result = "";

    foreach ($values as $value) {
        // 文字列なら " で囲む
        if (is_string($value)) {
            $formattedValue = '"' . $value . '"';
        } else {
            $formattedValue = $value;
        }

        if (empty($result)) {
            $result .= $formattedValue;
        } else {
            $result .= "," . $formattedValue;
        }
    }

    return $result;
}

これにより、count($values) の計算が不要になり、インデックス変数 $i の管理が不要になりました。

ステップ 3: 配列を活用(ローカル変数酷使度: 45)

「文字列を直接結合するのではなく、配列を使う方がわかりやすいかもしれない」と考え、次のように改善しました。

<?php

/** ローカル変数酷使度: 45 */
function createCsvLine3($values) {
    $columns = [];

    foreach ($values as $value) {
        // 文字列なら " で囲む
        if (is_string($value)) {
            $formattedValue = '"' . $value . '"';
        } else {
            $formattedValue = $value;
        }

        $columns[] = $formattedValue;
    }

    return implode(',', $columns);
}

配列 $columns を作り、それを implode でカンマ区切りの文字列に変換することで、処理が明確になりました。

ステップ 4: 変数のスコープをさらに縮小(ローカル変数酷使度: 15)

$formattedValue を毎回定義するのは冗長では?」 そう思い、三項演算子を使ってスッキリさせました。

<?php

/** ローカル変数酷使度: 15 */
function createCsvLine4($values) {
    $columns = [];

    foreach ($values as $value) {
        $columns[] = is_string($value) ? '"' . $value . '"' : $value;;
    }

    return implode(',', $columns);
}

コードの行数が減り、可読性が向上しました。

ステップ 5: array_map() を活用し、最適化(ローカル変数酷使度: 9)

foreachで行なっていることは、要するに $values の各値を変換して $columns を求めていることに気が付き「foreach すら不要では?」 と考え、array_map を利用することで、コードをさらに短縮しました。

<?php

/** ローカル変数酷使度: 6 + 3 = 9 */
function createCsvLine5($values) {
    return implode(',', array_map(function ($value) {
        return is_string($value) ? '"' . $value . '"' : $value;
    }, $values));
}

関数の内部で無名関数を使うことで、スコープ内の変数を最小限に抑え、処理が関数として完結しました。

まとめ

ステップ 方法 ローカル変数酷使度
1 for を使用、文字列連結あり 131
2 foreach を使用、文字列連結あり 106
3 配列を活用し implode() を使用 45
4 formattedValue を削除し簡潔化 15
5 array_map() を活用し最適化 9

ローカル変数酷使度を適切に管理することで、よりシンプルで読みやすいコードを書くことが可能 になるということがわかって貰えたのではないでしょうか?

注意点としては、ステップ5が理想のコードであるという主張ではなく、開発チームのメンテナンスしやすさの観点からステップ4までにするという決断をする場合もありえると思います。この記事の主張の中心は、ローカル変数を酷使しすぎるとプログラムが読み難くなり、ローカル変数酷使度という指標が改善の目安として使えるのではないかという点です。

ぜひ、あなたのプログラムでもリファクタリングの前後でローカル変数酷使度を計測して改善に役立ててみてください!

Discussion