PHP 8.0 の新機能を試してみよう

この記事は個人ブログと同じ内容です

www.ritolab.com


2020 年 11 月 26 日に PHP 8.0 が正式リリースとなりました。

今回は PHP 8.0 の新しい機能を使ってみようと思います。

実行環境

Docker でコンテナを作成して PHP 8.0 を動作させます。

今回は適当な場所に php80 というディレクトリを作成してそこで行っています。

php80/
└─ src/
    └─ index.php

以下のコマンドでコンテナを起動

docker run \
    -v /path/to/php80/src:/var/www/html \
    -p 8080:80 \
    -d --name test-php80-container php:8.0-apache

起動したコンテナの PHP バージョン

$ php -v
PHP 8.0.0 (cli) (built: Dec  1 2020 03:24:11) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.0-dev, Copyright (c) Zend Technologies

Named Arguments(名前付き引数)

関数のデフォルト値の一部を変更したい時とかに、この名前付きの引数を使用する事で引き数の指定が簡略化されます。

https://wiki.php.net/rfc/named_params

例えば以下のような関数があったとして

<?php

function sample(int $value = 1, int $value_int = 1, string $valueString = 'hoge')
{
    $number = $value + $value_int;

    return "{$valueString}: {$number}";
}

sample();
// => hoge: 2

3つの引き数にはデフォルト値が指定されているので、この関数を使用時に引き数を渡さなければそれぞれデフォルト値が代入されて、文字列 hoge: 2 が返ります。

この時に例えば「最後の引き数だけを指定したい」といった場合には 3 つ全ての引き数を指定してあげなければなりませんでした。

<?php

// 第三引数だけを変更したいけど第一引数と第二引数も渡してあげる必要がある
sample(1, 1, 'foo');
// => foo: 2

これを名前付き引き数を使用することで、第三引数のみを指定する事ができるようになりました。

<?php

// 第三引数の変数名を指定して引数として渡す
sample(valueString:'abc');
// => abc: 2

「最後の引数」というのは例なので、例えばこの関数の第二引き数のみを指定する事も可能です。

<?php

// 第二引数の変数名を指定して引数として渡す
sample(value_int: 10);
// => hoge: 11

複数指定することもできます。

<?php

// 複数指定する
sample(value_int: 5, valueString:'xyz');
// => xyz: 6

関数で定義されている引数の順番でなくてもいけます。

<?php

// 指定順がバラバラでもいける
sample(valueString:'AAA', value_int: 3, value: 4);
// => AAA: 7

Constructor property promotion(コンストラクタのプロパティ昇格)

クラスをインスタンス化する際にコンストラクタでセットするプロパティへの代入と定義が簡略化できるようになりました。

https://wiki.php.net/rfc/constructor_promotion

例えば、Member クラスをインスタンス化する際に id と name を渡すとすると、これまでは以下の記述で行っていました。

<?php

class Member
{
    public int $id;

    public string $name;

    public function __construct(int $id, string $name)
    {
        $this->id   = $id;
        $this->name = $name;
    }
}
  1. プロパティ(メンバ変数)を宣言する
  2. コンストラクタでそれぞれ id と name をプロパティへ代入する

これが PHP 8 からは以下のように簡略化して記述することができるようになりました。

<?php

class Member
{
    public function __construct(
        public int $id,
        public string $name,
    ) { }
}

コンストラクタの引数のところでプロパティを宣言しているようなイメージですね。

これで「プロパティの宣言」「コンストラクタで受け取る引数の型・順番の指定」「受け取った値をプロパティへ代入する」をまとめて行っている感じ。なんかすごいな。

Union types(Union型)

型宣言を複数記述することができるようになりました。

https://wiki.php.net/rfc/union_types_v2

関数の引数・戻り値、クラスのプロパティに型宣言が行えますが、これまでは型が1つに確定している場合のみ(もしくは ? で |nullを表現)記述できる状態でした。PHP 8 からは、複数の型を記述できるようになりました。

<?php

class Sample
{
    public int|float $a;

    public function set(int|float $a)
    {
        $this->a = $a;
    }

    public function get(): int|float|null
    {
        return $this->a;
    }
}

Match expression(match式)

match 式が追加されました。

switch 式と似ているけれど、より洗練された感じになっています。

<?php

$number = 1;


/* switch 式 */
switch ($number) {
    case 1:  $result = 'one';   break;
    case 2:  $result = 'two';   break;
    case 3:  $result = 'three'; break;
    default: $result = 'other';
}

echo $result;
// => one


/* match 式 */
$result = match ($number) {
    1       => 'one',
    2       => 'two',
    3       => 'three',
    default => 'other',
};

echo $result;
// => one

match が switch と違う点

  • 値を返す
  • 比較が厳密( == ではなく === )に行われる
  • フォールスルーしないので break を書かなくて良い

シンプルな分、間違いが起こりにくい印象を受けました。 比較部分を軽視してて予期せぬ挙動したりとか、単純に break 付け忘れて意図していない挙動するとか、そういうのは無くせそうですね。

Nullsafe operator(Null 安全演算子

JavaScript 等ではお馴染みな Null 安全演算子PHP でも使えるようになりました。

<?php

$country = $session?->user?->getAddress()?->country;

「?」をアローの手前に挟むやつです。

<?php

class User
{
    private int $id;

    private string $name;

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

    public function getName()
    {
        return $this->name;
    }
}

class Member
{
    public User|null $user = null;
}

$member = new Member();


$member->user->getName();
// => Fatal error: Uncaught Error: Call to a member function getName() on null

$member->user?->getName();
// => null

上記の例では、Member クラスの $userに User クラスがセットされていない場合、getName() を参照できないので Fatal error が発生しますが、 Null 安全演算子で記述することで、$member->user が null の場合は getName() を参照せずに null を返してくれるようになります。

str_contains()

対象文字列に、指定した文字列が含まれているかどうかを調べる str_contains() 関数が追加されました。

https://www.php.net/manual/ja/function.str-contains.php

<?php

$target = 'I love Grape,Apple,Orange,Strawberry and Cherry.';

$search = 'Orange';

str_contains($target, $search);
// => true

これまで strpos とか preg_match を使っていたことを考えたらシンプルになって良いかなと思います。

str_starts_with() / str_ends_with()

以下2つの関数も追加になっています。

<?php

$target = 'I love Grape,Apple,Orange,Strawberry and Cherry.';


$start = 'I';

str_starts_with($target, $start);
// => true

$end = 'Cherry.';

str_ends_with($target, $end);
// => true

他にもいろいろ

ここで取り上げたもの以外でも様々な変更点があるので PHP マニュアルを参照してみてください。

PHP: PHP 7.4.x から PHP 8.0.x への移行 - Manual