Kodokon kodokon.com

モダンPHP:厳密な型、列挙型、match

今日のPHPを取り入れましょう。厳密な型、アロー関数、nullsafe演算子、列挙型、そしてmatch式です。

9 分 · 3 問

このレッスンを Kodokon で開く

モダンPHPは型付けを大切にします。ファイルの最初の文として置かれたdeclare(strict_types=1)は、暗黙の変換を無効にします。文字列'10'floatのパラメーターに渡すと、黙って変換される代わりにTypeErrorが投げられます。パラメーターと戻り値に型を付けましょう。そうすればエラーは、三つ先のファイルではなく、早い段階の正しい場所で表面化します。

PHP
<?php

declare(strict_types=1);

function applyDiscount(
    float $price,
    float $rate = 0.1
): float {
    return round($price * (1 - $rate), 2);
}

echo applyDiscount(49.99);
echo PHP_EOL;
echo applyDiscount(100.0, 0.25);
型付けされたシグネチャ:関数の契約が明示的になる。

アロー関数fnは、単一の式からなる無名関数を凝縮します。従来のクロージャに対する大きな利点があります。use句なしで、親スコープの変数を自動的に捕捉するのです。array_map()array_filter()で真価を発揮します。

PHP
<?php

declare(strict_types=1);

$prices = [12.5, 49.9, 8.0, 120.0];
$rate = 0.2;

$discounted = array_map(
    fn (float $p): float => round($p * (1 - $rate), 2),
    $prices
);

$affordable = array_filter(
    $discounted,
    fn (float $p): bool => $p < 50
);

print_r($affordable);
rate変数がuse句なしで捕捉される。

nullsafe演算子?->は、鎖の中の一つのつながりがnullになった瞬間に、致命的エラーを起こす代わりに呼び出しを短絡させます。??と組み合わせれば、if文のピラミッドを、読みやすい一行に置き換えます。

PHP
<?php

declare(strict_types=1);

class Address
{
    public function __construct(
        public readonly string $city
    ) {
    }
}

class User
{
    public function __construct(
        public readonly ?Address $address = null
    ) {
    }
}

$alice = new User(new Address('Lyon'));
$bob = new User();

echo $alice->address?->city ?? 'Unknown city';
echo PHP_EOL;
echo $bob->address?->city ?? 'Unknown city';
Bobではエラーにならない:鎖は最初のnullで止まる。

列挙型は、閉じた値の集合(ステータス、ロール)に本物の型を与えます。存在しないステータスを作ることが不可能になります。そしてmatchswitchに対する明確な改良です。厳密な比較、値として返される結果、そしてどのケースも一致しなければUnhandledMatchError。見落としに対する安全策です。

PHP
<?php

declare(strict_types=1);

enum OrderStatus: string
{
    case Pending = 'pending';
    case Shipped = 'shipped';
    case Delivered = 'delivered';

    public function label(): string
    {
        return match ($this) {
            self::Pending => 'Pending',
            self::Shipped => 'Shipped',
            self::Delivered => 'Delivered',
        };
    }
}

$status = OrderStatus::from('shipped');
echo $status->label();
文字列に裏打ちされた列挙型:データベースに理想的。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. declare(strict_types=1)があるとき、float型のパラメーターに文字列'10'を渡すとどうなりますか?
    • PHPが黙って文字列を10.0に変換する
    • PHPが単なる警告を出して続行する
    • PHPがTypeErrorを投げる
  2. アロー関数fn()を、従来のクロージャfunction()と区別するものは何ですか?
    • 複数の文を含められる
    • use句なしで、親スコープの変数を自動的に捕捉する
    • 実行が速い
    • 型を宣言できない
  3. match式がswitchに対して持つ利点は何ですか?
    • 厳密に(===)比較し、値を返し、どのケースも一致しなければエラーを投げる
    • より柔軟にするために緩い比較(==)を受け入れる
    • あるケースから次のケースへのフォールスルーを許す