この記事は翻訳されたものです、元のアドレス: https://stitcher.io/blog/php-81-readonly-properties
PHP 8.1: 読み取り専用プロパティ
長年にわたって、PHP でのデータ転送オブジェクトと値オブジェクトの作成は非常に簡単になってきました。 PHP 5.6 の DTO を例として挙げます:
class BlogData { /** @var string */ private $title; /** @var Status */ private $status; /** @var \DateTimeImmutable|null */ private $publishedAt; /** * @param string $title * @param Status $status * @param \DateTimeImmutable|null $publishedAt */ public function __construct( $title, $status, $publishedAt = null ) { $this->title = $title; $this->status = $status; $this->publishedAt = $publishedAt; } /** * @return string */ public function getTitle() { return $this->title; } /** * @return Status */ public function getStatus() { return $this->status; } /** * @return \DateTimeImmutable|null */ public function getPublishedAt() { return $this->publishedAt; } }
そして、これを PHP 8.0 の同等の DTO と比較してください:
class BlogData { public function __construct( private string $title, private Status $status, private ?DateTimeImmutable $publishedAt = null, ) {} public function getTitle(): string { return $this->title; } public function getStatus(): Status { return $this->status; } public function getPublishedAt(): ?DateTimeImmutable { return $this->publishedAt; } }
これはかなり異なりますが、まだ大きな問題があると思います: これらすべてのゲッター。個人的には、PHP 8.0 とその改良されたプロパティ以降、これらを使用しなくなりました。私は、ゲッターを追加する代わりにパブリック プロパティを使用することを好みます。
class BlogData { public function __construct( public string $title, public Status $status, public ?DateTimeImmutable $publishedAt = null, ) {} }
オブジェクト指向の純粋主義者は、このアプローチを好みません。オブジェクトの内部状態は決して直接公開されるべきではなく、外部から変更することも絶対にできません。
Spatie のプロジェクトでは、パブリック プロパティを持つ DTO と VO を外部から変更してはならないという内部スタイル ガイド ルールがあり、これはうまく機能していると思われ、長い間これを実行してきました。しばらく経ちましたが、何も問題はありませんでした。
ただし、はい、言語によってパブリック プロパティがまったくオーバーライドされないことが保証される方がよいことに同意します。さて、PHP 8.1 では、readonly キーワードを導入することでこれらすべての問題を解決しました:
class BlogData { public function __construct( public readonly string $title, public readonly Status $status, public readonly ?DateTimeImmutable $publishedAt = null, ) {} }
このキーワードは基本的にその名前が示すとおりの動作を行います: プロパティが設定されると、それはオーバーライドできなくなります。 :
$blog = new BlogData( title: 'PHP 8.1: readonly properties', status: Status::PUBLISHED, publishedAt: now() ); $blog->title = 'Another title'; Error: Cannot modify readonly property BlogData::$title
オブジェクトが構築されると、そのオブジェクトは二度と変更されないことがわかっているため、コードを記述する際に一定の確実性と安心感が得られます。つまり、一連の予期せぬデータ変更が再び発生することはありません。
もちろん、データを新しいオブジェクトにコピーでき、場合によってはその過程でいくつかのプロパティを変更できるようにする必要があります。読み取り専用プロパティを使用してこれを行う方法については、この記事の後半で説明します。まず、それらを詳しく見てみましょう。
PHP 8.1 について詳しく知りたいですか? PHP 8.1 へのパスがあります。今後 10 日間、PHP 8.1 の新機能および既存の機能に関する電子メールが毎日届きます。その後は自動的に購読が解除され、スパムやフォローアップ メールは受信されなくなります。今すぐ購読してください!
#入力プロパティのみ
読み取り専用プロパティは、型指定されたプロパティと組み合わせてのみ使用できます:
class BlogData { public readonly string $title; public readonly $mixed; }
ただし、混合して使用することもできます。型ヒントとして:
class BlogData { public readonly string $title; public readonly mixed $mixed; }
この制限の理由は、コンストラクターで明示的な値が指定されていない場合、プロパティの型を省略すると、PHP がプロパティの値を自動的に null に設定するためです。この動作と読み取り専用を組み合わせると、不必要な混乱が生じる可能性があります。
#通常のプロパティとプロモートされたプロパティ
両方の例を見てきました: readonly は通常のプロパティとプロモートされたプロパティの両方に追加できます:
class BlogData { public readonly string $title; public function __construct( public readonly Status $status, ) {} }
#デフォルト値なし
読み取り専用プロパティはデフォルト値を持つことができません:
class BlogData { public readonly string $title = 'Readonly properties'; }
つまり、プロモートされたプロパティでない限り:
class BlogData { public function __construct( public readonly string $title = 'Readonly properties', ) {} }
許可されます。プロモートされたプロパティのデフォルト値はクラス プロパティのデフォルト値として使用されず、コンストラクターのパラメーターにのみ適用されるため、プロパティをプロモートします。上記のコードは舞台裏で次のように変換されます。
class BlogData { public readonly string $title; public function __construct( string $title = 'Readonly properties', ) { $this->title = $title; } }
実際のプロパティにデフォルト値が割り当てられていないことがわかります。ちなみに、読み取り専用プロパティのデフォルト値が許可されていない理由は、その形式の定数と何ら変わらないためです。
#レガシー
読み取り専用フラグは、継承中に変更することはできません:
class Foo { public readonly int $prop; } class Bar extends Foo { public int $prop; }
このルールは双方向です:読み取り専用フラグは変更できません。継承時に追加するか、フラグを削除します。
#設定解除は許可されていません
読み取り専用プロパティを設定すると、変更したり、設定を解除したりすることはできません:
$foo = new Foo('value'); unset($foo->prop);
#Reflection
新しいメソッドとフラグがあります。 ReflectionProperty::isReadOnly()ReflectionProperty::IS_READONLY
##Clone#したがって、読み取り専用プロパティを変更できず、設定解除もできない場合は、 DTO または VO のコピーを作成し、そのデータの一部を変更するにはどうすればよいですか?値を上書きできないため、クローンを作成することはできません。実際には、将来的にこの動作を可能にする構造を使用してクローンを作成するというアイデアがありましたが、それは現時点での問題を解決するものではありません。
そうですね、ちょっとした反射魔法に頼れば、変更された読み取り専用プロパティを持つオブジェクトをコピーできます。コンストラクターを呼び出さずにオブジェクトを作成し (これはリフレクションを使用して実行できます)、各プロパティを手動でコピーする (場合によっては値を上書きする) ことによって、実際にオブジェクトを「複製」し、その読み取り専用プロパティを変更できます。
これを行うための小さなパッケージを作成しました。次のようになります。
class BlogData { use Cloneable; public function __construct( public readonly string $title, ) {} } $dataA = new BlogData('Title'); $dataB = $dataA->with(title: 'Another title');
私は実際に、このすべての背後にあるメカニズムを説明する専用のブログ投稿を書きました。それはここで読むことができます。
読み取り専用プロパティについては以上です。多数の DTO や VO を扱い、コード全体のデータ フローを慎重に管理する必要があるプロジェクトに取り組んでいる場合、これらは素晴らしい機能だと思います。この点では、読み取り専用プロパティを持つ不変オブジェクトが非常に役立ちます。
以上がPHP8.1 の新機能を詳しく説明: 読み取り専用プロパティ 読み取り専用プロパティの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。