【问题标题】:Why does PHP call __set() after unset()ing declared typed property?为什么 PHP 在 unset() 声明类型属性后调用 __set()?
【发布时间】:2020-05-02 10:09:30
【问题描述】:

(编辑了这个问题,旧版本引用了RFC的错误版本。)

PHP 中的RFC for typed properties 表示“如果类型化属性是unset(),那么它将返回到未初始化状态。”但是,在 PHP 7.4(至少到 7.4.5)中,当类实现 __set() 时,它的行为似乎确实有所不同,因此它与(原始)未初始化状态不完全相同。第一次设置未初始化的属性时不会调用魔术函数,但在取消设置后再次设置属性时会(意外地)调用它。 Example code.

<?php

class X {
    public int $y;
    public function __set($name, $value) {
        echo "__set($name, $value) called";
        $this->$name = $value;
    }
}

$x = new X();
$x->y = 42; // Prints nothing, as expected.
unset($x->y);
$x->y = 43; // Prints `__set(y, 43) called`.

我是否忽略了什么?这是 PHP 当前如何为类型化属性实现 unset() 的错误吗?还是应该在第一次设置未初始化的类型属性时调用__set()

【问题讨论】:

  • 认为它是 - 当一个普通的对象属性未设置时,它会导致在随后访问时调用魔术获取......(在 RFC 中)
  • 那是关于魔法get,而我的问题是魔法set。我不确定你从哪里得到这句话,但来自 Progman 发布的链接:“如果一个类型化的属性处于未初始化状态,要么是因为它还没有被初始化,要么是因为它已经显式地 unset(),然后从此属性读取将调用 __get() 方法(如果存在),与普通属性的行为一致。”所以那句话的本意就是原来的未初始化状态和unset()之后的状态是等价的。
  • 这是同一个问题的另一个角度:在$x = new X() 之后,从$x-&gt;y 读取 将调用__get(),但写入它不会 调用__set(),不一致。

标签: php php-7.4


【解决方案1】:

为什么?

出于惰性初始化模式的目的。

如果一个类型化的属性是 unset(),那么它会返回到未初始化的状态。虽然我们很乐意删除对取消设置属性的支持,但此功能目前被 Doctrine 用于延迟初始化

所以在 PHP 8 中,在为延迟初始化提供了一些替代机制之后,它们可能会禁止取消设置已声明的属性。

【讨论】:

  • 所以,基本上你是说当前的行为可能很奇怪,但这没关系,因为我应该在没有unset() 的情况下编写我的应用程序(为 PHP8 做好准备或只是一般)?我想这行得通:-)
【解决方案2】:

Magic getter/setter 仅对不存在的属性触发。引用:https://www.php.net/manual/en/language.oop5.overloading.php#object.set -> __set() is run when writing data to inaccessible (protected or private) or non-existing properties.

所以在你的例子中,起初公共变量确实存在。取消设置后,公共变量不存在/未初始化,魔术 getter/setter 就位。

我认为这种行为仍然是错误的,因为该属性存在但未初始化。应该是一个错误报告?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多