PHPの便利な機能

 この記事では、PHPであまり使ったことのなかったけど使ってみたら便利だった機能を紹介します。

宇宙船演算子(Spaceship operator)`<=>`

 宇宙船演算子`<=>`は、PHP7.0から導入された比較演算子で、二つの値を比較し、その大小関係を示す値を返します。
 値が等しい場合は0を、左側の値が右側の値より大きい場合は1を、左側の値が右側の値より小さい場合は-1を返します。

echo 1 <=> 1; // 0
echo 3 <=> 2; // 1
echo 1 <=> 2; // -1

 この演算子は、ソート関数のコールバックなどで便利に使用できます。

$data = [3, 2, 5, 1, 4];
usort($data, function ($a, $b) {
    return $a <=> $b;
});
print_r($data); // 出力: [1, 2, 3, 4, 5]

 このコードは`$data`を昇順(小さい順)にソートします。
 `usort`はユーザー定義の比較関数を使って配列をソートする関数で、ここでは宇宙船演算子を使って比較を行っています。

Null合体演算子(Null coalescing operator)`??`

 Null合体演算子`??`は、PHP7.0から導入された演算子で、左辺の値が`null`でなければその値を、`null`であれば右辺の値を返します。`isset()`と組み合わせた場合と同じ効果がありますが、より簡潔に書くことができます。

$user_name = isset($_GET['user']) ? $_GET['user'] : 'Guest';

 このコードは`$_GET[‘user’]`が設定されているかをチェックし、設定されていればその値を`$user_name`に、設定されていなければ、`Guest`を`$user_name`に代入します。
 Null合体演算子を使用してこのコードを書くと、以下のようになります。

$user_name = $_GET['user'] ?? 'Guest';

Null合体代入演算子(Null coalescing assignment operator)`??=`

 Null合体代入演算子`??=`は、PHP7.4から導入された演算子で、左辺の変数が`null`である場合のみ右辺の値を代入します。Null合体演算子と同様に、`isset()`と組み合わせた場合と同じ効果がありますが、より簡潔に書くことができます。

$user_name = null;
$user_name ??= 'Guest'; // $user_nameがnullなので、'Guest'が代入されます。

echo $user_name; // 出力: 'Guest'

$user_name = 'User';
$user_name ??= 'Guest'; // $user_nameがnullでないので、何も起こらない

echo $user_name; // 出力: 'User'

 このコードは`$user_name = $user_name ?? ‘Guest’;`を短く書いた形です。

Null安全演算子(Null safe operator)`?->`

 Null安全演算子`?->`は、PHP8.0から導入された演算子で、オブジェクトのプロパティやメソッドに、安全にアクセスするためのものです。これを使うとオブジェクトが`null`の場合でもエラーを出さずに`null`を返すことができます。
 また、`null`が返る場合は、残りのチェインはスキップされます。`is_null()`を組み合わせた場合と同じ効果がありますが、より簡潔に書くことができます。

if(is_null($repository))
{
    $user_name = null;
}
else
{
    $user = $repository->getUser(5);
    if(is_null($user))
    {
        $user_name = null;
    }
    else
    {
        $user_name = $user->name;
    }
}

 このコードは`$repository`から指定したユーザーを取得しその`name`を取得します。
 Null安全演算子を使用してこのコードを書くと、以下のようになります。

$user_name = $repository?->getUser(5)?->name;

エルビス演算子

 エルビス演算子は三項演算子の一種です。
 条件式がtrueの場合の値を省略することで、条件式がtrueの場合、条件式の結果を値とします。
 以下の2式は同様の結果を示します。

$text = $value ? $value : 'default value';  // 三項演算子
$text = $value ?: 'default value';          // エルビス演算子

可変長引数

 `…`演算子で可変長引数の関数を実装できます。
 また、配列を展開することもできます。

// 可変長引数の関数
function add(int ...$operators)
{
    $result = 0;
    foreach($operators as $operator)
    {
        $result += $operator;
    }
    return $result;
}

echo add(2, 3, 4);

// 配列の展開
$operators = [2, 3, 4];
echo add(...$operators);

`__debugInfo()`メソッド

 `var_dump()`でオブジェクトの情報を出力する際のプロパティや、その値の表示方法を変更することができます。

class C {
    private $prop;

    public function __construct($val) {
        $this->prop = $val;
    }

    public function __debugInfo() {
        return [
            'propSquared' => $this->prop ** 2,
        ];
    }
}

var_dump(new C(42));

 このコードの出力は以下になります。

    object(C)#1 (1) {
    [“propSquared”]=>
    int(1764)

use宣言のグループ化

 use宣言のグループ化はPHP7.0から追加された記法です。
 複数のクラスや関数、定数を同じnamespaceからインポートする際に、単一のuse文にまとめることができます。

// 以前のバージョンのコード
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;

// PHP 7 以降のコード
use some\namespace\{ClassA, ClassB, ClassC as C};

アロー関数

 アロー関数はPHP7.4から追加された機能です。
 アロー関数を使うことで無名関数を簡潔に書くことができます。
 無名関数とは異なり、親のスコープの変数を常に自動で使うことができます。

$y = 1;

// 無名関数
$fn = function ($x) use ($y) {
    return $x + $y;
};

// アロー関数
$fn = fn($x) => $x + $y; // 親のスコープで定義されている$yを参照できる

名前付き引数

 関数呼び出し時に引数名で引数を渡すことができます。
 これにより引数を任意の順番で渡せるようになり、任意のデフォルト値を持つ引数をスキップすることができるようになります。

function foo($a, $b, $c = 3, $d = 4){
    return $a + $b + $c - $d;
}

// 以前は、cを省略してdを指定することはできなかった
echo foo(1, 2, 3, 5); // 1

// cは省略してdを指定できる
echo foo(1, 2, d:5); // 1

// 順番を入れ替えてもOK
echo foo(1, 2, d:5, c:3); // 1

コンストラクタの引数をプロパティへ昇格させる

 この機能はPHP8.0で追加されました。
 コンストラクタの引数を対応するオブジェクトのプロパティに昇格させることができます。
 また、コンストラクタの引数をプロパティに代入する記述を短縮できます。
 コンストラクタに処理を書いた場合は、引数の値が対応するプロパティに代入された後に追加の文が実行されます。

// 以前の書き方
class Point
{
    protected int $x;
    protected int $y;

    public function __construct(int $x, int $y)
    {
        $this->x = $x;
        $this->y = $y;
    }
}

// PHP8.0以降の書き方
class Point
{
    public function __construct(protected int $x, protected int $y){}
}

match式

 match式はPHP8.0で追加された機能です。
 値の一致をチェックした結果に基づいて評価結果を分岐させます。

$food = 'cake';

$return = match($food) {
    'cake' => 'This food is a cake',
    'chocolate' => 'This food is a chocolate',
    'ice cream' => 'This food is an ice cream',
};

var_dump($return);  // string(19) "This food is a cake"

 switch文と似ていますが、以下の点でいくつか違いがあります。
  ・match式の比較は、switch文が行う弱い比較`==`ではなく、厳密に値を比較`===`します。
  ・match式は値を返します。
  ・match式は全ての場合を網羅している必要があります。
  ・match式は必ずセミコロン`;`で終わらなければなりません。

 複数の分岐の右辺が同じ場合には、カンマを使って短縮して書くことができます。
 また、前の分岐にマッチしなかった、あらゆる場合にマッチするdefaultパターンも用意されています。

$result = match($x){
    1 => 'one',
    1, 3, 4 => 'two ,three or four', // 複数の分岐の右辺が同じ場合、カンマで区切って列挙できる
    default => 'something else',    // 前の分岐にマッチしなかった場合に実行される
};

 制約式に`true`を指定することで、厳密な一致チェックを行わずにmatch式を使うことができます。

$age = 23;

$result = match (true) {
    $age >= 65 => 'senior',
    $age >= 25 => 'adult',
    $age >= 18 => 'young adult',
    default => 'kid',
};

var_dump($result); // string(11) "young adult"

列挙型

 列挙型はPHP8.1で追加されました。
 列挙型を使うことで、取りうる値を限定した独自の型を定義することができます。
 列挙型のcaseには整数か文字列のスカラー値を持たせることができます。
 単一の列挙型が一度に持つことができる型はひとつだけです。(int|stringのようなunion型はサポートされていません。)
 スカラー値を持たないcaseをPure Caseと呼び、Pure Caseを持つ列挙型をPure Enumと呼びます。
 スカラー値を持つcaseをBack Caseと呼び、Back Caseを持つ列挙型をBacked Enumと呼びます。
 Pure EnumにはPure Caseのみを含めることができ、Back EnumにはBack Caseのみを含めることができます。
 Back Enumでは全てのcaseはユニークなスカラーの値を明示的に定義する必要があります。
 それぞれのcaseはその名前がついたシングルトンオブジェクトであり、`<`や`>`で値の比較はできません。
 列挙型にはnameやvalueなどのプロパティやリストを取得するcases()メソッドが用意されています。
 列挙型には関数を定義することができます。

enum ShipmentStatus: int
{
    case None = 0;
    case Adjustment = 1;
    case Shipped = 2;
    case Returned = 3;
    case Cancel = 4;

// 関数を定義することもできる
    public function getName(): string
    {
        return match($this){
            self::None => '出荷待',
            self::Adjustment => '調整中',
            self::Shipped => '出荷済',
            self::Returned => '返却済',
            self::Cancel => 'キャンセル',
        };
    }
}

// 名前の取得
echo ShipmentStatus::Shipped->name; // Shipped

// スカラー値の取得
echo ShipmentStatus::Shipped->value; // 2

// 関数を定義することもできる
echo ShipmentStatus::Shipped->getName(); // 出荷済

// スカラー値からEnumを取得する
echo ShipmentStatus::from(2)->name; // Shipped
echo ShipmentStatus::from(10)?->name; // ValueError
echo ShipmentStatus::tryFrom(2)->name; // Shipped
echo ShipmentStatus::tryFrom(10)?->name; // null

// Enumの値を比較する
echo ShipmentStatus::Shipped === ShipmentStatus::Shipped; // true
echo ShipmentStatus::Shipped === ShipmentStatus::None; // false
echo ShipmentStatus::Shipped > ShipmentStatus::None; // false(オブジェクトの比較を行っているため)
echo ShipmentStatus::Shipped->value > ShipmentStatus::None->value; // true(スカラー値の比較を行っているため)

// Enumのリストを取得する
foreach(ShipmentStatus::cases() as $status)
{
    echo $status->getName();
}

 お役に立てましたでしょうか? もし少しでも役に立ったのでしたら、いいね! もしくは、SNSなどで共有してくださると嬉しいです。

 最後まで読んで下さりありがとうございました。
 
 

お問い合わせはこちら