【PHP】PHP8の新機能を具体的なコードで理解する

PHP

1. 概要

WEBサービスやホームページでよく使われているPHPですが、まだまだ進化をし続けています。

今回は、PHP7までしか知らなかった私が、PHP8の新機能を紹介します。

2. 名前付き引数

名前の通り、関数を呼ぶときの引数に、名前をつけられるようになりました。

まず、下記の関数を見てください。

function hello($sei = '孫', $mei = '悟空')
{
    echo 'こんにちは、' . $sei . $mei . 'さん!' . "\n";
}

デフォルト値を設定した関数定義の場合、PHP7以前では2つ目を変えたい場合に第1引数も設定する必要がありました。

// 孫悟空さんに挨拶したい場合
hello(); // 第1、第2引数両方にデフォルト値があるので、引数無しでもOK
hello('孫'); // 第2引数にデフォルト値があるのでOK
hello('孫', '悟空'); // 両方指定しているのでOK

// 孫悟飯さんに挨拶したい場合
// 第1引数を指定しないと、第2引数を指定できない
hello('孫', '悟飯');

PHP8以降では、$meiの値を指定して関数を呼び出すことができるようになりました。

// こんにちは、孫悟飯さん!
hello(mei: '悟飯');

いくつかの他言語ではサポートされている機能ですが、ついにPHPにも来ましたね。すばらしい!

例)Pythonで書く場合↓

def hello(sei='孫', mei='悟空'):
    print(f"こんにちは、{mei}{sei}さん!")

hello(mei='悟飯')

3. Attribute

とりあえず、公式の文章を載せます。

アトリビュートを使うと、 コンピューターが解析できる構造化されたメタデータの情報を、 コードの宣言時に埋め込むことができます。 つまり、クラス、メソッド、関数、パラメータ、プロパティ、クラス定数にアトリビュートを指定することができます。 アトリビュートで定義されたメタデータは、 実行時に リフレクションAPI を使って調べることが出来ます。 よって、アトリビュートは、 コードに直接埋め込むことが出来る、 設定のための言語とみなすことができます。

アトリビュートを使うと、機能の抽象的な実装と、アプリケーションでの具体的な利用を分離できます。 この点でアトリビュートは、インターフェイスとその実装と比較できます。 インターフェイスとその実装はコードに関する情報ですが、 アトリビュートはコードの追加情報と設定に注釈を付けるものです。 インターフェイスはクラスによって実装できますが、 アトリビュートはメソッドや関数、パラメータ、プロパティ、クラス定数で宣言できます。 よって、アトリビュートはインターフェイスより柔軟です。

アトリビュートの概要 – Manual

はい、ピンときませんでした。

アトリビュート(属性)を付与できるようになったとのことで、管理しやすくなるイメージでしょうか?

サンプルコードを見ていきましょう。

<?php

#[Attribute]
class Hashira
{
    private readonly string $name;
    private readonly string $role;

    /**
     * @param string $name 名前
     * @param string $role 役職
     */
    public function __construct(string $name, string $role)
    {
        $this->name = $name;
        $this->role = $role;
    }

    /**
     * 名前を取得
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * 役職を取得
     * @return string
     */
    public function getRole(): string
    {
        return $this->role;
    }
}

#[Hashira(name: '冨岡義勇', role: '水柱')]
class TomiokaGiyu
{
}
$tomiokaGiyuRef = new ReflectionClass(TomiokaGiyu::class);
$tomiokaGiyuAttrs = $tomiokaGiyuRef->getAttributes();

// Hashira(アトリビュート名)
echo $tomiokaGiyuAttrs[0]->getName();

// Array
// (
//     [name] => 富岡義勇
//     [role] => 水柱
// )
print_r($tomiokaGiyuAttrs[0]->getArguments());

// インスタンスも生成できる
// 富岡義勇:水柱
$tomiokaGiyuInstance = $tomiokaGiyuAttrs[0]->newInstance();
echo $tomiokaGiyuInstance->getName() . ':' . $tomiokaGiyuInstance->getRole();

「Hashira」という属性セットを適用させたクラス「TomiokaGiyu」を作ってみました。

富岡義勇さんのクラス内にはメンバ変数やメソッドがありませんが、リフレクションにより利用することができました。

PHP 8ではよく使うものでしょうか?

7までの人間にとっては、abstractinterfaceで良いのでは??と思ったのが正直なところです。

抽象クラスで書いたものがこちらです。

<?php

abstract class Hashira
{
    private readonly string $name;
    private readonly string $role;

    /**
     * @param string $name 名前
     * @param string $role 役職
     */
    public function __construct(string $name, string $role)
    {
        $this->name = $name;
        $this->role = $role;
    }

    /**
     * 名前を取得
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * 役職を取得
     * @return string
     */
    public function getRole(): string
    {
        return $this->role;
    }
}

class TomiokaGiyu extends Hashira
{
}

$tomiokaGiyu = new TomiokaGiyu('富岡義勇', '水柱');
echo $tomiokaGiyu->getName() . ':' . $tomiokaGiyu->getRole();

4. コンストラクタのプロモーション

クラスのインスタンス生成時に、コンストラクタで受け取った引数をメンバ変数へ格納することがよくあると思います。だいたい同じ変数名ですし、面倒に感じることも多かったと思いますが、一括で定義出来るようになりました。

これは便利ですね。

abstract class Hashira
{
    private readonly string $name;
    private readonly string $role;

    public function __construct(string $name, string $role)
    {
        $this->name = $name;
        $this->role = $role;
    }
}

↓
abstract class Hashira
{
    public function __construct(private string $name, private string $role)
    {
        // 不要
    }
}

5. union型のサポート

変数の型はintやstringなど1つのみでしたが、複数持たせられるようになったようです。

class Foo {
    /**  
    * @var int|float $num
    */  
    private $num;

    /**  
    * @var int|float $a
    * @var int|float $b
    * @return void
    */  
    public function test($a, $b): void
    {
        $this->num = $a * $b;
    }

    public function echoNum(): void
    {
        echo $this->num;
    }
}

↓

class Foo {
    private int|float $num;

    public function test(int|float $a, int|float $b): void
    {
        $this->num = $a * $b;
    }
    public function echoNum(): void
    {
        echo $this->num;
    }
}

$aと$bはintでもfloatでも受け付けられるようになりました。

正直なところ、型の違いでバグにつながりそうという印象でした。便利な使い道があれば記事にしようと思います。

6. match式のサポート

値によって返り値を変えたい場合に使えるものです。

$name = '胡蝶しのぶ';

// 胡蝶しのぶは蟲柱です
echo $name . 'は' . match ($name) {
    '冨岡義勇' => '水柱です',
    '胡蝶しのぶ' => '蟲柱です',
    '煉獄杏寿郎' => '炎柱です',
    default => '柱ではありません'
};

6-1. if / elseif / elseとの違い

if文のゴリ押しでも同じことはできますが、コードが長くなります。

柱は9人いますので、書くコードはその分だけ増えます。matchの方がスマートですね!

$str = $name . 'は';
if ($name === '冨岡義勇') {
    $str .= '水柱です';
} else if ($name === '胡蝶しのぶ') {
    $str .= '蟲柱です';
} else if ($name === '煉獄杏寿郎') {
    $str .= '炎柱です';
} else {
    $str .= '柱ではありません';
}
echo $str;

6-2. switchとの違い

matchはswitchに近いですが、比較が厳密かどうかが違います。

matchは型もチェックする厳密な比較(===)に対し、switchはゆるやかな比較(==)となります。

今回は文字列の比較なので挙動は同じですが、switchは0, ‘0’, false等のバグの温床となるような値たちには注意が必要でした。

matchを使えば型も比較対象となるので、万一、型が怪しい送られ方をされても厳密に比較してくれます。

ただし、あくまで今回のように返り値を返すシンプルな場合に対して使えるものなので、分岐した後にいろいろ処理を書くことに注意です。

一応switchの場合のコードも記載します。

$str = $name . 'は';
switch ($name) {
    case '冨岡義勇':
        $str .= '水柱です';
        break;
    case '胡蝶しのぶ':
        $str .= '蟲柱です';
        break;
    case '煉獄杏寿郎':
        $str .= '炎柱です';
        break;
    default:
        $str .= '柱ではありません';
        break;
}
echo $str;

7. nullsafe 演算子

Nullへの扱いの選択肢が増えたようです。is_nullを使ってもいいですが、シンプルな分岐の場合は、よりスマートに書けるようになりました。

例としてDBからユーザー情報を取得するとき、何かしらで失敗した場合はnullにすることがあると思います。

is_nullで判定してもいいのですが、今回追加されたnullsafe演算子を使うことにより、1行で完結できます。

// NULL
$user = null;
var_dump($user?->name);

// 竈門炭治郎
$user = (object) ['name' => '竈門炭治郎'];
var_dump($user?->name);

8. 最後に

今回はPHP7のエンジニアが8の目玉となる新機能を紹介しました。

新機能だけでなく変更点もいくつもあるので、別の記事にて紹介します!

9. 参考

PHP: 新機能

PHP8.0のUNION型で型指定の幅が広がった

投稿者プロフィール

KatoShingo
学んだことをアウトプットしていきます!
好きなこと:音楽鑑賞🎵 / ドライブ🚗 / サウナ🧖

関連記事

  1. PHP

    【PHP】PHP7から8への移行のために知るべきこと

最近の記事

  1. PHP
  2. PHP
  3. TypeScript
  4. Docker
  5. AWS
  6. Docker

制作実績一覧

  1. Checkeys