25、PHP 8.4 新特性 - Asymmetric Visibility(不对称可见性)
RFC: https://wiki.php.net/rfc/asymmetric-visibility
文档:https://www.php.net/manual/en/language.oop5.visibility.php#language.oop5.visibility-members-aviz
PHP 8.4 引入了一个非常有趣的特性——Asymmetric Visibility(不对称可见性),它允许为类的属性和方法指定不同的可见性修饰符,以使其在不同的上下文中具有不同的访问权限。这一特性对于一些特殊的场景(例如需要对外公开某些方法,但不希望让其他方法直接访问类中的某些属性)非常有用。
语法
{read visibility} {set visibility}(set) {propertyType} $name;
- 如果未提供读取可见性,则假定为
public
。 - Set (write) 可见性始终后跟字符串 “
(set)
”。 - 写入可见性必须等于或低于读取可见性。
- 当与属性钩子结合使用时,可见性适用于相应的 get 和 set 钩子。
- 非对称可见性要求属性具有类型声明。
class Foo
{
public private(set) string $bar;
}
此代码声明了一个属性 $bar
,该属性可以从 public
范围读取,但只能从 private
范围进行修改。也可以将其声明为 protected(set)
以允许从任何受保护
范围 (即子类) 设置属性。
背景和传统的可见性修饰符
在 PHP 中,类的属性和方法的访问权限通常由以下几个可见性修饰符来控制:
-
public
:可以在任何地方访问。 -
protected
:只能在当前类和继承的子类中访问。 -
private
:只能在当前类内部访问。
这些修饰符适用于类的方法和属性,但是 Asymmetric Visibility 特性允许为方法和属性分配不同的可见性修饰符来控制访问。
基本使用
<?php
declare(strict_types=1);
class Foo
{
public protected(set) int $bar = 0;
public function test()
{
$bar = &$this->bar;
$bar++;
}
}
$foo = new Foo;
$foo->test();
echo $foo->bar;
// PHP Fatal error: Uncaught Error: Cannot modify protected(set) property Foo::$bar from global scope in
// $foo->bar = 3;
非对称属性可见性
<?php
declare(strict_types=1);
class Book
{
public function __construct(
public private(set) string $title,
public protected(set) string $author,
protected private(set) int $pubYear,
) {}
}
class SpecialBook extends Book
{
public function update(string $author, int $year): void
{
$this->author = $author; // OK
$this->pubYear = $year; // Fatal Error
}
}
$b = new Book('西游记', '吴承恩', 2024);
echo $b->title; // 西游记
echo $b->author; // 吴承恩
// echo $b->pubYear; // Fatal Error
$b->title = '红楼梦'; // Fatal Error
$b->author = '曹雪芹'; // Fatal Error
$b->pubYear = 2023; // Fatal Error
关于不对称可见性,有一些注意事项:
- 只有类型化属性才能具有单独的
set
可见性。 -
设置
的可见性必须与get
相同或更具限制性。那是 publicprotected(set)
和protected protected(set)
是允许的,但受保护的 public(set)
会导致语法错误。 - 如果属性是
public
,则可以省略 main visibility。即public private(set)
和private(set)
将得到相同的结果。 - 具有
private(set)
可见性的属性自动成为final
,并且不能在子类中重新声明。 - 获取对属性的引用遵循
set
visibility,而不是get
。 这是因为引用可用于修改属性值。 - 同样,尝试写入数组属性时,会同时涉及
get
和set
操作,因此将遵循set
visibility 的 intent,因为这总是更严格的。
注意: set-visibility 声明中不允许使用空格。
private(set)
是正确的。private( set )
不正确,将导致解析错误。
非对称属性继承
class Book
{
protected string $title;
public protected(set) string $author;
protected private(set) int $pubYear;
}
class SpecialBook extends Book
{
public protected(set) $title; // OK, as reading is wider and writing the same.
public string $author; // OK, as reading is the same and writing is wider.
public protected(set) int $pubYear; // Fatal Error. private(set) properties are final.
}
请登录后再评论