PHP8.1の新機能を試してみた

# はじめに
PHP8.1の新機能を試してみました。
試した機能は以下です。
* 文字列をキーとして持つ配列のアンパック
* 列挙型
* 交差型

それではそれぞれの詳細と具体例を書いていきたいと思います。

# 1. 文字列をキーとして持つ配列のアンパック
文字列のキーを持つ配列を展開できるようになりました。
```php
<?php
$arr1 = ['foo' => 'foo'];
$arr2 = [...$arr1, 'bar' => 'bar'];
var_dump($arr2);
```

```php
// 実行結果
array(2) {
  ["foo"]=>
  string(3) "foo"
  ["bar"]=>
  string(3) "bar"
}
```
この機能のおかげで連想配列が扱いやすくなりそうです。

# 2. 列挙型
列挙型は独自の型を定義するための機能で、セレクトボックスで設定する値に使用したり、バリデーションに使えるかと思います。

ここでは定義方法からセレクトボックスの値として使用する方法まで具体例を書きたいと思います。

## 2.1 定義する
下記では会員ランクの型を定義しています。
```php
<?php
enum MembersRank
{
    case Regular;
    case Silver;
    case Gold;
    case Platinum;
}

function foo(MembersRank $members_rank)
{
	return $members_rank;
}

var_dump(foo(MembersRank::Platinum));
```

```php
// 出力
enum(MembersRank::Platinum)
```
上記のコードは`MembersRank`型を定義し、その型の値が4つ設定されているような状態です。
`boolean`型に`true`と`false`の値があるようなイメージです。

下記で説明されています。

https://www.php.net/manual/ja/language.enumerations.overview.php


`MembersRank`型の有効な値は以下の4つになります。
* `MembersRank::Regular`
* `MembersRank::Silver`
* `MembersRank::Gold`
* `MembersRank::Platinum`

上記以外は許容しませんので下記のように`foo`の引数に文字列を渡すと`MembersRank`型にしなさいと怒られてしまいます。

```php
<?php
enum MembersRank
{
    case Regular;
    case Silver;
    case Gold;
    case Platinum;
}

function foo(MembersRank $members_rank)
{
	return $members_rank;
}

var_dump(foo('bar'));
```
```php
// エラー
Fatal error: Uncaught TypeError: foo(): Argument #1 ($members_rank) must be of type MembersRank, string given, called in /home/user/scripts/code.php on line 14 and defined in /home/user/scripts/code.php:10
```
今のところどう使えばいいか想像がつきにくいと思いますが、
例を見ていくと徐々にわかってくるのでご安心ください。

## 2.2 セレクトボックスの値として使用する
セレクトボックスの値として使用するまでに以下の手順でコードを書いていきます。
1. caseにラベリングする
1. 全てのcaseを配列で取得する
1. casesを利用して設定した文字列を全て取得する


### 2.2.1 caseにラベリングする
それぞれの`case`に対して、整数や文字列で値を設定することができます。
下記では文字列を設定しています。
```php
<?php
enum MembersRank: string // 型を宣言する
{
	case Regular = 'レギュラー';
    case Silver = 'シルバー';
    case Gold = 'ゴールド';
    case Platinum = 'プラチナ';
    
    function get_members_rank()
	{
		return $this->value;// 定義した値を取得
	}
}

function foo(MembersRank $members_rank)
{
	return "あなたは{$members_rank->get_members_rank()}メンバーです。";
}

$value = "Platinum";
echo foo(constant("MembersRank::{$value}"));
```

```
// 出力
あなたはプラチナメンバーです。
```

`$this->value`で設定した値を取得することができます。
ちなみに値を設定した場合は型を宣言しないとエラーになってしまうでお忘れなく。

### 2.2.2 全てのcaseを配列として取得する
`cases`メソッドを使用すると全ての`case`を配列として取得することができます。
`cases`は列挙型に対してあらかじめ定義されています。
```php
<?php
enum MembersRank: string
{
    case Regular = 'レギュラー';
    case Silver = 'シルバー';
    case Gold = 'ゴールド';
    case Platinum = 'プラチナ';
}

var_dump(MembersRank::cases());
```

```php
// 出力
array(4) {
  [0]=>
  enum(MembersRank::Regular)
  [1]=>
  enum(MembersRank::Silver)
  [2]=>
  enum(MembersRank::Gold)
  [3]=>
  enum(MembersRank::Platinum)
}
```

### 2.2.3 casesを利用して設定した文字列の値を全て取得する
`cases`で取得した配列を`foreach`で回して値を配列に格納していきます。

```php
<?php
enum MembersRank: string
{
    case Regular = 'レギュラー';
    case Silver = 'シルバー';
    case Gold = 'ゴールド';
    case Platinum = 'プラチナ';

	static public function get_label_list()
	{
		$list = [];
		foreach(MembersRank::cases() as $case) {
			array_push($list, $case->value);
		}
		return $list;
	}
}

var_dump(MembersRank::get_label_list());
```

```php
// 出力
array(4) {
  [0]=>
  string(15) "レギュラー"
  [1]=>
  string(12) "シルバー"
  [2]=>
  string(12) "ゴールド"
  [3]=>
  string(12) "プラチナ"
}
```

上記のように取得した値をセレクトボックスで使用することができます。
列挙型を使用するとあらかじめ値を限定した状態にできるため不正な値が入りづらくなります。



# 3. 交差型
下記の記事を参考にさせていただきました。

https://qiita.com/rana_kualu/items/f49582bc94e0bae23d9c

交差型はクラス型、インターフェース型を、アンド条件で型宣言できる機能です。
`A&B`と宣言すると、AとBを満たした場合のみ許容するようになります。

以下の例では`get_class_inherits_AB`関数がクラスのインスタンスを返します。
そのインスタンスが複数のインタフェース`A`、`B`を継承しているかを交差型でチェックしています。

```php
<?php
interface A
{
	public function A();
}

interface B
{
	public function B();
}

class foo implements A, B
{
	public function A() {
		return 'A';
	}
	
	public function B() {
		return 'B';
	}
}

function get_class_inherits_AB($type): A&B { // 交差型で宣言
	if ($type === 'foo') {
		return new foo();
	}
}

function bar($foo) {
	return $foo->A() . "\n" . $foo->B();
}

echo bar(get_class_inherits_AB('foo'));
```
主にクラスにインタフェースが継承されているかのチェックに使用できそうです。

# 4. おわりに
ここまで読んでいただきありがとうございました。
試した機能の数は少なかったですが、どれもガンガン使っていけそうな便利な機能でした。


# 5. 参考

https://www.php.net/manual/ja/migration81.new-features.php

https://www.php.net/manual/ja/language.types.declarations.php#language.types.declarations.composite.intersection

https://www.php.net/manual/ja/language.enumerations.basics.php

https://qiita.com/rana_kualu/items/f49582bc94e0bae23d9c