PHP只读属性?

maz*_*iak 58 php programming-languages

在使用PHP的DOM类(DOMNode,DOMEElement等)时,我注意到它们拥有真正的只读属性.例如,我可以读取DOMNode的$ nodeName属性,但我无法写入它(如果我做PHP抛出致命错误).

如何在PHP中创建自己的只读属性?

too*_*php 43

你可以这样做:

class Example {
    private $__readOnly = 'hello world';
    function __get($name) {
        if($name === 'readOnly')
            return $this->__readOnly;
        user_error("Invalid property: " . __CLASS__ . "->$name");
    }
    function __set($name, $value) {
        user_error("Can't set property: " . __CLASS__ . "->$name");
    }
}
Run Code Online (Sandbox Code Playgroud)

只有在你真正需要的时候才使用它 - 它比普通的属性访问慢.对于PHP,最好采用仅使用setter方法从外部更改属性的策略.

  • 确实!__get方法非常慢.我有一个使用类似这样的配置类,因此可以访问每个私有变量但不能更改.当我通过分析器运行我的代码时,我对它消耗的时间感到震惊:(真的很难过.我希望php具有readonly属性. (6认同)
  • 几年过去了.新的PHP版本发布.PHP中的__get方法是否仍然太慢?我正在使用PHP 7.0 (3认同)

Jso*_*owa 36

从 PHP 8.1 开始,实现了本机只读属性

文档

在属性声明期间,您只能初始化只读属性一次。

class Test {
    public readonly string $prop;

    public function __construct(string $prop) {
        $this->prop = $prop;
    }
}
Run Code Online (Sandbox Code Playgroud)

--

class Test {
    public function __construct(
        public readonly string $prop,
    ) {}
}
Run Code Online (Sandbox Code Playgroud)

尝试修改 readonly 属性将导致以下错误:

Error: Cannot modify readonly property Test::$prop
Run Code Online (Sandbox Code Playgroud)

更新 PHP 8.2

从 PHP 8.2 开始,您可以将其定义为readonly整个类。

readonly class Test {
    public string $prop;

    public function __construct(string $prop) {
        $this->prop = $prop;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在继承方面,使用新的 readonly 关键字与 const 关键字的最大区别是显而易见的。在 8.1 之前,常量不会被继承,因此只能由它自己的类访问。任何继承的类都必须再次实现此常量或使用parent关键字来调用父类,并且无法在初始化时设置不同的值。 (4认同)
  • 链接到常规文档:https://www.php.net/manual/en/language.oop5.properties.php#language.oop5.properties.readonly-properties (2认同)

小智 12

但是,仅使用__get()公开的私有属性对于枚举对象成员的函数是不可见的 - 例如json_encode().

我经常使用json_encode()将PHP对象传递给Javascript,因为它似乎是传递具有从数据库填充的大量数据的复杂结构的好方法.我必须在这些对象中使​​用公共属性,以便将这些数据填充到使用它的Javascript中,但这意味着这些属性必须是公共的(因此存在另一个程序员不在相同波长上的风险(或者可能)我自己经历了一个糟糕的夜晚)可能会直接修改它们.如果我将它们设为私有并使用__get()和__set(),则json_encode()不会看到它们.

拥有"只读"辅助功能关键字不是很好吗?

  • 实现JsonSerializable接口的类可以通过jsonSerialize方法定义自定义属性来进行编码。您可以在此处显示要编码的私有属性。 (3认同)

Le *_*exo 6

这是一种从外部呈现类的所有属性 read_only 的方法,继承的类具有写访问权限;-)。

class Test {
    protected $foo;
    protected $bar;

    public function __construct($foo, $bar) {
        $this->foo = $foo;
        $this->bar = $bar;
    }

/**
 * All property accessible from outside but readonly
 * if property does not exist return null
 *
 * @param string $name
 *
 * @return mixed|null
 */
    public function __get ($name) {
        return $this->$name ?? null;
    }

/**
 * __set trap, property not writeable
 *
 * @param string $name
 * @param mixed $value
 *
 * @return mixed
 */
    function __set ($name, $value) {
        return $value;
    }
}
Run Code Online (Sandbox Code Playgroud)

在 php7 中测试

  • @mrReiha,?? 如果参数_存在_并且不为空_,则返回左侧表达式。否则它返回右手表达式。它充当由两部分组成的 if() 语句: if (isset(expr1) && !is_null(expr1)) { return expr1; } else { 返回 expr2; }。 (3认同)

小智 5

我看到你已经得到了答案,但对于那些仍在寻找的人:

只需将所有"readonly"变量声明为private或protected,并使用魔术方法__get(),如下所示:

/**
 * This is used to fetch readonly variables, you can not read the registry
 * instance reference through here.
 * 
 * @param string $var
 * @return bool|string|array
 */
public function __get($var)
{
    return ($var != "instance" && isset($this->$var)) ? $this->$var : false;
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我还保护了$ this-> instance变量,因为此方法将允许用户读取所有声明的变量.要阻止多个变量,请使用带有in_array()的数组.