[PHP8.x] 我要引入非对称可见性

Swift 允许您为读取和写入设置不同的可见性。

private (set) public var count: Int = 0 // 読み込みはpublic、書き込みはprivate

这就是为什么肆无忌惮的 PHP 也试图将这个特性引入 PHP 的原因。

以下是相关的RFC,不对称可见性这是一个介绍。

不对称可见性

介绍

PHP 长期以来一直有能力控制属性的可见性。

但是 get 和 set 的功能总是相同的。

该 RFC 建议为属性读取和写入操作提供不同的可见性。

具体来说,它主要是从 Swift 中借来的。

提议

提供新的set 语法,用于声明属性写入操作的可见性。

class Foo
{
    public private(set) string $bar;
}

此属性$bar 具有公共读取范围,但具有私有写入范围。

protected(set) 也使它成为受保护的范围。

此属性的行为与常规属性完全相同,只是它的可见性有限。

属性集可见性也可以与构造函数参数提升一起使用。

class Foo
{
    public function __construct(
        public private(set) string $bar,
    ) {}
}

参考

只能从可写范围获得对具有设置可见性的属性的引用。

换句话说,引用遵循写入可见性,而不是读取可见性。

class Foo
{
    public protected(set) int $bar = 0;
 
    public function test() {
        // OK 同クラスなのでprivateは見える
        $bar = &$this->bar;
        $bar++;
    }
}
 
class Baz extends Foo
{
    public function stuff() {
        // OK 子クラスなのでprotectedは見える
        $bar = &$this->bar;
        $bar++;
    }
}
 
$foo = new Foo();

// OK
$foo->test();
 
// これもOK
$baz = new Baz();
$baz->stuff();

// NG privateには外からアクセスできない
$bar = &$foo->bar;

对象属性

如果目标属性是对象,则设置可见性仅适用于对象更改。

它不会影响对象本身。

这也与只读属性的行为相匹配。

class Bar
{
    public string $name = 'beep';
}
 
class Foo
{
    public private(set) Bar $bar;
}
 
$f = new Foo();
 
// OK これは許される
$f->bar->name = 'boop';
 
// NG Foo::$barは外から見れない
$f->bar = new Bar();

允许的可见性

如果 get 和 set 具有不同的可见性,则 set 必须具有比 get 更窄的可见性。

也就是说,可以为集合设置的唯一可见性是protectedprivate

如果get 的可见性是protected,那么可以为set 设置的唯一可见性是private

任何违规都会导致编译错误。

与 __set 的交互

当类中定义了魔术方法__set 并且从非法范围访问它时,将调用__set

这是为了在一个类中为其他目的实现__set 时尽可能防止出现意外行为。

对于只读实现也是如此。

所以我们对不对称可见性有相同的行为。

如果读取可见性不匹配,则调用 __set

如果写入可见性不匹配,则为错误。

class Foo {
    protected private(set) string $prot;
    public private(set) string $pub;

    public function __set($name, $value) {
        var_dump($name, $value);
    }
}

$foo = new Foo();

$foo->prot = 'Foo'; // __setが呼ばれる

$foo->pub = 'Foo'; // エラーになる

与只读的关系

public private(set) 可能看起来与 readonly 相同,但实际上有点不同。

readonly 不允许从public 写入,并且只能从private 写入一次。

public private(set) 不允许从 public 写入,并且允许从 private 无限写入。

您不能在单个属性上同时使用 readonly 和不对称可见性。

类型化的属性

非对称可见性仅适用于具有显式类型的属性。

这主要是由于实现复杂性造成的限制。

具有未知值的属性可以是混合类型,所以这应该不是一个实际问题。

反射

向 ReflectionProperty 添加了两个方法:isProtectedSet():boolisPrivateSet():bool

意思是不言自明的。

class Test
{
    public string $open;
    public protected(set) string $restricted;
}
 
$rClass = new ReflectionClass(Test::class);
 
$rOpen = $rClass->getProperty('open');
print $rOpen->isProtectedSet() ? 'Yep' : 'Nope'; // Nope
 
$rRestricted = $rClass->getProperty('open');
print $rRestricted->isProtectedSet() ? 'Yep' : 'Nope'; // Yep

还添加了常量ReflectionProperty::IS_PROTECTED_SETReflectionProperty::IS_PRIVATE_SET

这些被视为从ReflectionProperty::getModifiers() 返回的任何其他可见性修饰符。

向后不兼容的更改

没有向后不兼容的更改。

建议的 PHP 版本

PHP8.3?

未来范围

本节具有前瞻性,不包含在本 RFC 中。

替代操作

目前,唯一可以限定范围的操作是写入和读取。

未来可能会增加其他操作,例如以下。

init__construct__clone__unserialize 等属性只能从初始化过程中设置。

once。只能写一次。 public private(once)readonly 完全相同,但public protected(once) 也可由子类写入。

unset。允许取消设置属性。

额外的可见性

如果引入了包级可见性等,public package(set) 等将可用。

属性访问器

对于不对称可见性,我们之前有属性访问器被提议作为 RFC

相关的 RFC 是使用 C# 语法作为模型编写的。

另一方面,这个 RFC 借用了 Swift 的语法,与 C# 风格相比,它有两个优点。

• 减少混乱,因为所有可见性都在一个地方。

- 属性访问器 RFC 面临的问题不受影响。

本 RFC 不排除或限制未来引入属性访问器。

参考

Swift 中的相同功能.

想法

虽然 Nikita 曾经打算将其引入 PHP8.1,辞职属性访问器语法替代。

RFC 说它不会被淘汰,但由于功能几乎相同,如果采用此 RFC,那么引入属性访问器就没有什么意义了。

RFC前几天刚提交,所以还不知道会发生什么,但是我个人认为readonly涵盖了大部分内容,包括属性访问器,所以不知道这个RFC有多少需求. 不是。

此外,private public(set) 无法定义。

我不确定您可以编写但无法读取的属性是什么意思。

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308629114.html