PHP8の機能を確認してみよう

PHP8.1が2021年11月にリリースされました。

一体どんな機能が追加されたのでしょう。

PHP8.0のリリースから1年以上が経過した今、これからPHP8の導入を検討する方も多くいらっしゃるのではないでしょうか。

本記事では新しく追加・変更されたPHP8.0とPHP8.1の機能をいくつかピックアップして、コード例とともに紹介していきます。

目次

  • PHP8
    • 名前付き引数
    • アトリビュート
    • コンストラクタでの、プロパティのプロモーション
    • 読み取り専用プロパティ
    • Union型
    • Match 式
    • Enum型
    • Nullsafe 演算子
    • 数値と文字列の比較
  • おわりに

PHP8

まずはPHP公式のサイトを確認してみます( https://www.php.net/releases/8.0/ja.php )

PHP 8.0 は、PHP 言語のメジャーアップデートです。 このアップデートには、たくさんの新機能や最適化が含まれています。 たとえば 名前付き引数、 union 型、アトリビュート、コンストラクタでのプロパティのプロモーション、match 式、nullsafe 演算子、JIT、型システムの改善、エラーハンドリング、一貫性の向上などです。

公式に説明がある通りPHP8にはたくさんの機能が追加されています。

試しにphp-srcリポジトリでPHP-7.0とPHP-8.0を比較し( https://github.com/php/php-src/compare/PHP-7.0...PHP-8.0 )、 新機能の名前である「Attribute」で検索してみると、機能の実装やテストコードが新たに追加されているのがみてとれますし、PHP7.4のソースコードと比較するとざっと45万行のコードが新たに追加されているようです。

ここからは実際に追加・変更された機能を確認します。

名前付き引数

関数を呼び出す際に仮引数の変数名を使用して値を渡す事ができます。

<?php

function hoge($name, $age, $height){}

hoge(name:”太郎”, age:50, height: 150);

コンストラクタで名前付き引数を使用することもできるため、インスタンス作成時に大量の情報を受け取るようなクラスでは有用です。

アトリビュート

クラスや関数により多くの情報を定義・宣言することができる機能です。

アトリビュートを使わずに関数を宣言すると

<?php

function hoge(string $text): int {}

のようになります。プログラム上では関数の名前や引数・返り値の型などの情報を取得する事ができますが、より多くの情報を関数に付与したい場合はどうでしょうか。

関数をグルーピングしたい場合や別名をつけてあげたい場合、プログラム中での重要度をつけたい場合など、そういった何かメタデータのようなものを関数に付与できる、それがアトリビュートです。

使い方ですが、はじめに付与したい情報をクラスとして定義します。ここでは重要度として$importanceをプロパティに持つクラスを定義します。 クラス定義の上部に書かれている「#[Attribute]」は、このクラスがアトリビュートのためのクラスであることを宣言しています。

<?php

#[Attribute]

class Metadata {

    public int $importance;

    public function __construct(int $importance) {

        $this->importance = $importance;

    }

}

次にメタデータをつけたい対象にメタデータをつけます。

今回は関数doAnythingに先ほど定義したMetadetaをimportanceを10としてつけてみます。これで関数doAnythingには「重要度が10である」という情報が新たに加わりました。

<?php

#[Metadata(importance: 10)]

function doAnything(){}

最後に付与されたメタデータを読み取ります。

ReflectionAPIを使用して取得します。

<?php

$reflection = new ReflectionFunction("doAnything");

foreach($reflection->getAttributes() as $attribute) {

    print_r($attribute->newInstance());

}

print_rの出力結果です。

きちんと関数doAnythingに設定したMetadataインスタンスが取得できていることが確認できます。

Metadata Object
(
    [importance] => 10
)

このようにAttributeは関数やクラスに付加情報を与えるための機能です。

Attributeを設定できるものは、メソッドや関数、クラス、引数、プロパティ、クラス定数です。

引数にAttributeを設定するには以下のように書きます。

function example(#[Metadata(importance: 10)] string $arg) {}

コンストラクタでの、プロパティのプロモーション

コンストラクタの引数でプロパティを宣言する事ができます。

引数にアクセス権の修飾子がついているものは自動的にプロパティとなる仕様です。

下のコードのCarインスタンスのプロパティには「public float $speed」と「protected string $name」が自動的に追加されます。「int $color」がプロパティにならないのはアクセス権の修飾子がついていないためです。

<?php

class Car {

    public function __construct(

        public float $speed = 10.0,

        protected string $name = "car",

        int $color = 0xffffff

    ) {}

}

$car = new Car();

print_r($car);

print_rの出力です。

きちんとアクセス修飾子をつけた引数のみがプロパティになっている事が確認できます。

Car Object
(
    [speed] => 10
    [name:protected] => car
)

読み取り専用プロパティ

「readonly」をつけて宣言することで、プロパティを読み取り専用として宣言することができます。

<?php

class Foo {

    public function __construct(int $number) {

        $this->number = $number;

    }

}

プロパティのプロモーションと合わせて使用することでよりスリムになります。

<?php

class Bar {

    public function __construct(public readonly int $number) {}

}

Union型

複数の異なる型のうちどれか1つだけを持つ事ができるようになる型です。 「TypeA|TypeB|TypeC」のようにパイプで複数の型を列挙して書きます。 関数の引数にUnion型を指定するコードを示します。

<?php

function typeTest(int|bool $arg) {};

この関数の引数はintかboolどちらか一方を受け付けますが、stringを渡すと下に示すような内容のFatal errorを引き起こします。

「Fatal error: Uncaught TypeError: typeTest(): Argument #1 ($arg) must be of type int|bool, string given」(strict_types=1にて実施) また、mixed型は「object|resource|array|string|int|float|bool|null」と同じです。

メソッドの引数やプロパティであればself型やparent型を使用することもできます。

Match 式

switch文と似ています。制約式と評価式を比較して条件分岐を行う事ができます。

match式は「式」なので結果を返す事ができるため、下のコードを実行すると「received text n」が表示されます

<?php

$result = match("text") {

    "number" => "received a number ",
    "text" => "received text",

};

print($result);

switch文と異なり厳密な比較を行い、マッチした時点でmatch式から抜けます(値を返します)。

条件式を複数書くこともでき、その場合は「or(論理和)」として評価されます。

最後までマッチしない場合には、例外(UnhandledMatchError)が投げられますが、条件に「default」キーワードを書くことで回避する事ができます。

<?php

$statusCode = 200;

$message = match (true) {

    $statusCode >= 200 && $statusCode < 300 => "Success!!",
    default => "Error!!",

};

print($message);

Enum型

「イーナム」と発音します。

取りうる値を限定した独自の型を定義できます。

classやtraitのように宣言を行います。

Enumは0個以上のcaseを持つことができ、各caseはシングルトンです。

<?php

enum Food {

    case Pizza;

    case Donuts;

    case Chips;

}

function eat(Food $food) {

    var_dump($food);

}

eat(Food::Chips);

Enumを使用することで取りうる範囲を限定することできるため、コード全体の保守性を高めることができます。

論理型のような離散的で範囲の絞れるような定数をまとめたい場合にはEnumを使用すると良いです。

Enumはメソッドの追加やinterfaceの実装をしたり、各caseに値を入れることができます。

以下に「信号」をイメージしたEnumを示します。

<?php

enum Signal : int {

    case Green = 0b100;
    case Yellow = 0b010;
    case Red = 0b001;

    public static function fromFlag(int $flag) : Signal {


        return match ($flag) {

            static::Green->value => static::Green,
            static::Yellow->value => static::Yellow,
            static::Red->value => static::Red,

        };

    }

    public function nextSignal() : Signal {

        return match ($this) {

            self::Green => self::Yellow,
            self::Yellow => self::Red,
            self::Red => self::Green,

        };
                
    }

}

$signal = Signal::Green;

var_dump($signal);

var_dump(Signal::fromFlag(0b001)->nextSignal()->nextSignal());

Nullsafe 演算子

呼び出しチェインの中でnullが返された場合に残りのチェインを呼び出さずにチェイン全体の値としてnullを返すことができます。

「呼び出しまたはアクセスしたものがnullの場合」を条件にする場合のネストを浅くすることができます。

5回以上呼び出すとnullを返す状況を作り確認してみます。

最後のvar_dumpでは「six」でnullにアクセスしていますが、Nullsafe演算子「?->」にすることによって例外は投げられずにnullが返ってきます。

<?php

class Hoge {

    private const LIMIT = 5;

    private int $num = 0;

    private function returnThis() : Hoge|null {

        $this->num ++;

        return $this->num < self::LIMIT ? $this : null;

    }

    public function __get($name) {

        return $this->returnThis();

    }

    public function __call($name, $arguments) {

        return $this->returnThis();

    }

}

$hoge = new Hoge();

var_dump($hoge->one->two()->three()->four);

$hoge = new Hoge();

var_dump($hoge->one->two()->three()->four->five);

$hoge = new Hoge();

var_dump($hoge->one->two()->three()->four->five?->six?->seven);

またsix,sevenで$hogeにアクセスすることはしません。

sixの箇所は以下と同じ状況です。

var_dump(null?->six);

数値と文字列の比較

数値形式の文字列と比較する場合は、PHP 8 は数値として比較を行います。

それ以外の場合は、数値を文字列に変換し、文字列として比較を行います。

<?php

var_dump(0 == "0"); //true

var_dump(0 == "0.0"); //true

var_dump(0 == "foo"); //false

var_dump(0 == ""); //false

var_dump(42 == "   42"); //true

var_dump(42 == "42jfdklsa"); //false

おわりに

PHP8.0とPHP8.1で追加された機能をいくつか紹介してみましたがいかがでしょうか。

メジャーアップデートなだけあって、すぐに慣れるには時間がかかりそうですが、コードの見通しや保守性を大きく向上させることのできるアップデートでもあると思います。

紹介した以外の機能や廃止された機能など、他にも変更点はありますので、ぜひ公式サイトを確認しながら実際にコーディングしてみることをおすすめします。

Webサイト・システムの
お悩みがある方は
お気軽にご相談ください

お問い合わせ 03-6380-6022(平日09:30~18:30)

出張またはWeb会議にて、貴社Webサイトの改善すべき点や
ご相談事項に無料で回答いたします。

無料相談・サイト診断 を詳しく見る

多くのお客様が気になる情報をまとめました、
こちらもご覧ください。