いつのまにやらObject InitializerというRFCが投票に入っていました。
ちょっとだけ面白そうと思ったのですが、ただ、ほぼ確実に却下されるので詳しく見てもしょうがないのでざっくり紹介してみます。
Object Initializer
文法
class Customer{
public $id;
public $name;
private DateTimeImmutable $createdAt;
}
$customer = new Customer{
id = 123,
name = "John Doe",
};
newするときに中括弧で引数を渡すと、自動的にプロパティにセットされます。
キーが文字列ではないところが、PHPとして物凄い違和感がありますね。
上の例は、下のようなよくある文と同等です。
$customer = new Customer();
$customer->id = 123;
$customer->name = "John Doe";
従って、privateである$createdAt
に値を突っ込むことはできません。
制約
オブジェクト初期化子を使う場合、全てのpublicプロパティを指定しなければなりません。
$customer = new Customer{
id = 123, // RuntimeException class object failed due to missing required properties
};
オブジェクト初期化子自体を使わない場合は、普通にインスタンス化できます。
$customer = new Customer();
未定義プロパティ
未定義のプロパティに値を突っ込めます。
$baz = 'baz';
$obj = new stdClass {
foo = "bar",
$baz = true,
};
ええー、と思いますが、そもそもこれPHPの仕様だったわ。
コンストラクタ
オブジェクト初期化子を使う場合、コンストラクタに引数は渡せません。
即ち、以下のような書き方は文法エラーになります。
$customer = new Customer($dateTime){
id = 123,
name = "John Doe",
};
マジックメソッド
可視プロパティがなかった場合、普通にマジックメソッド__set
が呼ばれます。
RFCに書かれている以下の例では、$name
はpublicなので直接値が入り、protectedである$email
はマジックメソッド__set
が呼ばれることになるようです。
class EmailAddress
{
protected string $email;
public ?string $name = null;
public function __set(string $name, $value): void
{
if ($name !== "email") {
return;
}
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("Invalid email address");
}
$this->email = $value;
}
}
$email = new EmailAddress {
email = "john.doe@example.org",
name = "John Doe",
};
将来の予定
無名クラスstdClass
を使うときは、もうクラス名すらも書かなくてよくない?
$obj = new {
foo = "bar",
$baz = true,
};
配列展開してpublicプロパティ突っ込めたらよくない?
$array = [
'a' => 1,
'b' => 2,
];
$obj = new { ...$array };
投票
2019/10/07投票開始、2019/10/21投票終了、可決には2/3+1の賛成が必要です。
2019/10/09現在は賛成3反対18で、ほぼ確実に却下されます。
この投票期間、RFCには何故か書かれていません。
感想
うん、まあ却下だよね。
特にpublicプロパティは全て指定しなければならないという厳格さと、未定義プロパティを指定できるという緩さが同居してるのは気持ち悪いというかなんというか。
これならまだAutomatic Property Initializationのほうがいいと思います。
Code free constructorよりはいいと思いますが。
ただ無名クラスはすごい便利そうなので、これは欲しいところですね。
$obj = new{
'id' = 123,
'name' = 'John Due',
};
まあ、今でもほぼ同じように書けたりはするんですけどね。
$obj = (object)[
'id' => 123,
'name' => 'John Due',
];