11
Help us understand the problem. What are the problem?

posted at

【PHP8.2】readonlyなクラスが作れるようになる

PHP8.1でreadonlyプロパティが実装されましたが、このreadonly修飾子がクラスにも使えるようになります。
プロパティごとにreadonlyを設定するのではなく、クラス自体を厳格に運用することができるようになります。

以下は該当のRFC、Readonly classesの日本語訳です。

PHP RFC: Readonly classes

Introduction

PHP8.1でreadonlyプロパティがサポートされました。
しかしたとえば、多くのプロパティのある不変クラスの宣言は未だに面倒です。

そこで、このRFCではreadonlyなクラスのサポートを提案します。

Proposal

PHP8.1で追加されたreadonlyを、クラスにも適用できるようにします。

PHP8.2
readonly class Test {
    public string $prop;
}

これによって、クラス内の全てのプロパティがreadonlyになります。
また、動的プロパティの作成も禁止されます。

readonly class Foo{
    public int $bar;

    public function __construct() {
        $this->bar = 1;
    }
}

$foo = new Foo();

$foo->bar = 2; // Fatal Error: Uncaught Error: Cannot modify readonly property Foo::$bar
$foo->baz = 1; // Fatal Error: Uncaught Error: Cannot create dynamic property Foo::$baz

またDeprecate dynamic propertiesのRFCにおいては、#[AllowDynamicProperties]を指定することで動的プロパティを引き続き使えるようにサポートされましたが、readonlyクラスに対しての指定は禁止されます。

#[AllowDynamicProperties]
readonly class Foo {
}

// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo

Restrictions

型のついていないプロパティ、静的プロパティを宣言することはできません。

readonly class Foo{
    public $bar;            // Fatal error: Readonly property Foo::$bar must have type
    public static int $baz; // Fatal error: Readonly class Foo cannot declare static properties
}

Inheritance

readonlyプロパティと同様、readonlyクラスをreadonlyクラスでextendsすることは可能です。

readonly class A {}
readonly class B extends A {} // これはOK

readonlyクラスをreadonlyでないクラスでextendsしたり、その逆は禁止されます。

readonly class A {}
class B extends A {}
// Fatal error: Non-readonly class B cannot extend readonly class A

class A {}
readonly class B extends A {}
// Fatal error: Readonly class B cannot extend non-readonly class A

Reflection

ReflectionClass::isReadOnly()メソッドが追加され、クラスがreadonlyか否かを調べることができます。
またReflectionClass::getModifiers()メソッドにReflectionClass::IS_READONLYフラグが追加されます。

Backward Incompatible Changes

互換性のない変更はありません。

Vote

投票期間は2022/04/27から2022/05/11まで。

このRFCは、賛成28反対7の賛成多数で受理されました。

感想

一回だけ代入可能であり、代入したあとでの変更は不可能になります。
要するにimmutableオブジェクトです。

readonly class Foo{
    public function __construct(
        public int $bar
    ) {}
}

$foo = new Foo(1); // OK
$foo->bar = 2; // NG

readonlyを指定したクラスは、静的プロパティは設定できず、動的プロパティも使用できず、型指定は必須であり、一度設定した値を書き替えることはできないと、できることが非常に限定されるようになります。
そのぶん何処かでうっかり値を書き替えられるなどの事故を防ぐことができ、堅固で安全性の高いインスタンスの運用が可能になります。

未定義プロパティへのアクセスは最終的にPHP9.0で禁止されるのですが、readonlyクラスはそれのほぼ上位互換みたいなものです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
11
Help us understand the problem. What are the problem?