$circle->area • $circle->circumference •静的解析を利⽤可能 •仕様追加も容易 • set を後から実装すれば良い class Circle { public function __construct( public float $radius ) { } public float $area { get { return pi() * $this->radius ** 2; } } public float $circumference { get { return 2 * pi() * $this->radius; } } }
• あるのは posts() メソッド (リレーションシップ) •Laravelがリレーションシッ プを「フック」として呼び 出し「投稿⼀覧」を取得 class User extends Model { public function posts() { return $this->hasMany(Post::class); } } class PostController { public function index(User $user) { return $user->posts; } }
• $this->area を参照しない 2025-06-28 PHP 8.4の新機能「プロパティフック」から学ぶ オブジェクト指向設計とリスコフの置換原則 18 class User { public string $name { get => ucfirst($this->name); } } class Circle { public float $area { get => pi() * $this->radius ** 2; } } フックの中で⾃⾝で同名の実体を正確に参照していればバックドプロパティ
仮想プロパティなので $this->>foo には実体が無い • 存在しないもの参照しようとしエラー class Example { public string $foo { get { $temp = 'foo'; return $this->$temp; // 展開すると $this->foo } } } new Example()->foo; // Uncaught Error: Must not read from virtual property Example::$foo
バックドプロパティでの &get と set の併⽤は禁⽌ • 仮想プロパティでは併⽤できる(set する実体が無いため) class User { public ?string $name = null { &get { return $this->name; } set(?string $value) { $this->name = strtolower($value); } } } // Fatal error: // Get hook of backed property User::name with set hook may not return by reference
string $name; public function __construct( string $name, ) { $this->name = $name; } } class User { public function __construct( private string $name, ) { } } class User { public function __construct( public string $name { get => ucfirst($this->name); set => strtolower($value); } ) { } } 型宣⾔を コンストラクタへ移動 移動した先へ 同じようにフックを定義
「広げる」ことは可能 • 「狭める」「変更する」のは不可能 class User { public DateTime $birthDate { set(string $value) { $this->birthDate = new DateTime ($value) ->setTime(0, 0); } } } // Type of parameter $value of hook User::$birthDate::set // must be compatible with property type set の型を「変更した」場合 型エラー
ここではPHP上の型は変わらない(共に int 型) class Integer { public function __construct( public int $value, ) { } } $integer = new Integer(-1); // 問題なし class NaturalNumber extends Integer { public int $value { set { assert($value > 0); $this->value = $value; } } } $naruralNumber = new NaturalNumber(-1); // Uncaught AssertionError: // assert($value > 0)
function __construct( public int|float $value ) { } } class Integer extends Number { public int|float $value { set { assert(floor($value) === (float)$value); $this->value = $value; } } } $integer = new Integer(-1); // 問題なし $integer = new Integer(-1.1); // Uncaught AssertionError: // assert(floor($value) === (double)$value) ¥PHPStan¥dumpType(new Integer(1)->value) // int|float int に変更したい 変更できない(何故?) Type of Integer::$value must be int|float (as in class Number)
• フックのは⾃⾝の実体を参照しない • set操作を持たない • getしか実装していない • 仮想プロパティなのでデフォルトの set 操 作(単なる代⼊)も無い • 「出⼒」しかない • 以上より、共変である • = 型を「狭める」ことができる class Number { public function __construct( protected int|float $_value ) { } public int|float $value { get => $this->_value; } } class Integer extends Number { public int $value { get => (int)$this->_value; } } 狭める