📘

命令型プログラミング言語のPHPで、宣言的なコードを書く

に公開

こんにちは!レバテック開発部のきょうかです。
今回は「PHPにおける宣言的コード」がテーマです。

私は最近Reactに触れ始めたのですが、そこで「宣言型」という言葉に出会いました。
Reactは宣言的にUIを記述できるライブラリで、それが大きな特徴のひとつにもなっています。
一方で、私が普段から主に使用しているPHPは、一般的に命令型プログラミング言語として認識されています。
そのため、PHPは宣言的に書くことはできないと思い込んでいました。
しかし色々と調べていくうちに、PHPも書き方を工夫することで宣言的なスタイルを取り入れられると知りました。
この違いや、PHPで宣言的にコードを書く具体的な方法について、これから詳しく解説していきます!

命令型、宣言型とは

まずは、それぞれのプログラミングパラダイムの基本的な考え方を整理しましょう。

  • 命令型プログラミング
    • やりたいことを「どうやって(How)」やるか、具体的な手順を細かく記述する書き方
  • 宣言型プログラミング
    • 何を(What)」達成したいか、最終的な結果や状態を記述する書き方
      • 具体的な手順や内部の処理は、利用するフレームワークやライブラリ、言語機能に任せる

命令型と宣言型の比較

タクシーに乗った時の運転手への伝え方を例に、命令型と宣言型で比較してみます。

  • 命令型の場合(具体的に指示をする)
    • 「3つ目の信号を右に曲がってください。」
    • 「そのまま300メートル進むと左手にコンビニが見えるので、そこで左折してください。」
    • 「まっすぐ進んだ先に見える赤い屋根のお店の前で止めてください。」
  • 宣言型の場合(目的地のみ伝える)
    • 「〇〇区〇〇-〇〇にある⚪︎⚪︎⚪︎というお店までお願いします。」

宣言型のメリットとは

上記の例でわかるように、宣言型の方が記述が簡潔になります。
また指示をする側は、細かい手順を知っておく必要がありません。

プログラミングにおいても、宣言型のアプローチは命令型よりも可読性、保守性が高いと言われています。
コードの意図が明確になり、「何をするのか」が分かりやすくなります。

PHPで宣言的に書いてみる

ここからは、実際のPHPコードで命令的な書き方と宣言的な書き方を見比べていきましょう。

データ抽出を宣言的にする

「引数で受け取った配列のうち、enable_flagがtrueのものだけ返すメソッド」を命令的、宣言的双方で書いていきます。
◾️命令的

function getEnableValues(array $array): array
{
    $enable_array = [];
    foreach ($array as $value) {
        if ($value->enable_flag === true) {
            $enable_array[] = $value;
        }
    }
    return $enable_array;
}

◾️宣言的

function getEnableValues(array $array): array
{
    return array_filter($array, function ($value) {
        return $value->enable_flag === true;
    });
}

命令型では、「返却用の配列を用意し、ループで各要素を巡回し、条件に一致したものをその配列に追加する」といった手順が詳細に書かれていましたが、宣言型の方ではarray_filter()を使うことで抽出したい条件を書くだけで良くなりました。

データ加工を宣言的にする

「ユーザーデータを画面表示用に加工して返すメソッド」を命令的、宣言的双方で書いていきます。
◾️命令的

function getUserViewData(array $users): array
{
    $use_view_data = [];
    foreach ($users as $user) {
        $use_view_data[] = [
            'id' => $user->id,
            'name' => $user->first_name . ' ' . $user->last_name,
        ];
    }
    return $use_view_data;
}

◾️宣言的

function getUserViewData(array $users): array
{
    return array_map(function (User $user) {
        return [
            'id' => $user->id,
            'name' => $user->first_name . ' ' . $user->last_name,
        ];
    }, $users);
}

こちらの例もひとつめのものと同様です。
配列をループしデータを加工する詳細な手順が書かれている命令型に対し、宣言型ではarray_map()を使うことで「元の配列を基に、画面表示用のデータ形式に変換する」という目的を直接的に表現できるようになりました。

条件分岐を宣言的にする

「ステータスコードに合わせてエラーメッセージを返すメソッド」を命令的、宣言的双方で書いていきます。
◾️命令的

function getErrorMessage(int $status): string
{
    switch ($status) {
        case 400:
            $message = '不正なリクエストです。';
            break;
        case 401:
            $message = '認証に失敗しました。';
            break;
        case 403:
            $message = 'アクセス権限がありません。';
            break;
        case 500:
            $message = 'サーバー内部でエラーが発生しました。';
            break;
        default:
            $message = '予期しないエラーが発生しました。';
            break;
    }
    return $message;
}

◾️宣言的

enum ErrorStatus: int
{
    case BadRequest = 400;
    case Unauthorized = 401;
    case Forbidden = 403;
    case InternalServerError = 500;
}

function getErrorMessage(ErrorStatus $status): string
{
    return match ($status) {
        ErrorStatus::BadRequest => '不正なリクエストです。',
        ErrorStatus::Unauthorized => '認証に失敗しました。',
        ErrorStatus::Forbidden => 'アクセス権限がありません。',
        ErrorStatus::InternalServerError => 'サーバー内部でエラーが発生しました。',
        default => '予期しないエラーが発生しました。',
    };
}

switch()での処理をenumのmatch()に変更しました。
enumのメリットについてはぜひ以下の記事も参考にしてみてください。
https://zenn.dev/levtech/articles/d68e23d74a963d

上記3つの例から分かるように、宣言的なコードの方がシンプルな実装になっていますね!
命令型のコードでは、「どのように(How)」処理を進めるか(「ループで回す」「条件に一致したら値を配列に追加する」「処理が終わったらbreakする」など)を具体的に指示していますが、宣言型のコードでは「何を(What)」達成したいか(「条件に合った値を抽出する」「各要素を変換する」など)を直接的に表現する形になっています。
これにより、コードの可読性が上がり意図も汲み取りやすくなっています。

今回ご紹介した例は、PHPで宣言的にコードを書く一例に過ぎません。
他にも様々な書き方や、フレームワークの提供する機能(例えばLaravel Collectionなど)を活用することで、さらに宣言的なスタイルを追求できます。

常に宣言的に書く方が良いのか

宣言的な書き方をすると、シンプルな実装になり可読性、保守性が向上します。
しかし常にその書き方が最適であるとは限りません。

特にPHPのようなインタプリタ言語では、関数呼び出しや一時的なメモリ確保が影響し、命令的な書き方よりもわずかながらオーバーヘッドが生じやすくなります。
そのため非常に複雑な処理をする場合や大量のデータを扱う場合は、パフォーマンス面も考慮しつつどちらの書き方が最適かを都度判断する必要があります。

まとめ

いかがでしたでしょうか。
array_filter()array_map()など、普段何気なく使っているメソッドや構文が、PHPを宣言的なコードにするための方法のひとつであることがわかりました。
配列を駆使している処理のコードをいま一度見直してみると、宣言的な書き方に置き換えられる部分が見つかるかもしれません。

ただし、PHPの場合は宣言的な書き方が常に正というわけではありません。
パフォーマンス面も考慮しながら書き方を変えていきましょう。

補足

宣言的にコードを書くスタイルを追求する上で、関数型プログラミングの知識も非常に役立ちます。
今回ご紹介したarray_filter()array_map()高階関数と呼ばれるもので、関数型プログラミングと密接に関連する概念です。
今回の記事では触れませんが、また別の機会に記事に起こしたいと思います。

参考

レバテック開発部

Discussion