我使用PHP的DOM类(DOMNode,DOMEElement等)时注意到它们具有真正的只读属性。例如,我可以读取DOMNode的$nodeName属性,但我不能写入它(如果我这样做,PHP会抛出致命错误)。
我如何在PHP中创建自己的只读属性?
我使用PHP的DOM类(DOMNode,DOMEElement等)时注意到它们具有真正的只读属性。例如,我可以读取DOMNode的$nodeName属性,但我不能写入它(如果我这样做,PHP会抛出致命错误)。
我如何在PHP中创建自己的只读属性?
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");
}
}
只在你确实需要时使用它 - 它比普通的属性访问要慢。对于PHP,最好采用一种只使用setter方法从外部更改属性的策略。
__get
,这只能在内部(扩展中)实现。 - Artefacto__set()
方法,因为PHP会自动捕获私有属性。另一种方法是将属性设置为public
,然后仅使用__set()
方法来捕获任何尝试设置属性的操作。此时不需要__get()
方法,因为可以直接读取属性。 - Jason您只能在属性声明期间初始化只读属性。
class Test {
public readonly string $prop;
public function __construct(string $prop) {
$this->prop = $prop;
}
}
class Test {
public function __construct(
public readonly string $prop,
) {}
}
尝试修改只读属性会导致以下错误:Error: Cannot modify readonly property Test::$prop
更新 PHP 8.2
自 PHP 8.2 版本起,您可以将整个类定义为 readonly
。
readonly class Test {
public string $prop;
public function __construct(string $prop) {
$this->prop = $prop;
}
}
但是只使用__get()公开的私有属性对于枚举对象成员的函数来说是不可见的,例如json_encode()。
我经常使用json_encode()将PHP对象传递给Javascript,因为它似乎是传递从数据库填充了大量数据的复杂结构的好方法。在这些对象中,我必须使用公共属性,以便将数据传递到使用它的Javascript中,但这意味着这些属性必须是公共的(因此存在另一个程序员直接修改它们而不考虑意图的风险)。如果我将它们设置为私有并使用__get()和__set(),则json_encode()将无法看到它们。
是否有"readonly"可访问性关键字会更好呢?
$pleaseTouch
与 $_doNotTouch
来表示是否应该依赖于给定的属性。 - Seldom 'Where's Monica' NeedyJsonSerializable
接口的类可以通过定义 jsonSerialize
方法来自定义要编码的属性,从而将私有属性序列化。在该方法中,您可以指定要编码的私有属性。 - Taufik Nurrohmanclass 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;
}
}
已在PHP7中进行测试
我看到您已经得到了答案,但是对于仍在寻找答案的人:
只需将所有“readonly”变量声明为私有或受保护的,并使用魔术方法__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;
}
如您所见,我还保护了$this->instance变量,因为此方法将允许用户读取所有声明的变量。要阻止几个变量,请使用一个包含在数组中的in_array()。
对于那些正在寻找一种将私有/受保护属性暴露出来以进行序列化的方法,如果您选择使用getter方法使其只读,那么这里有一种方法可以实现这一点(@Matt:以json为例):
interface json_serialize {
public function json_encode( $asJson = true );
public function json_decode( $value );
}
class test implements json_serialize {
public $obj = null;
protected $num = 123;
protected $string = 'string';
protected $vars = array( 'array', 'array' );
// getter
public function __get( $name ) {
return( $this->$name );
}
// json_decode
public function json_encode( $asJson = true ) {
$result = array();
foreach( $this as $key => $value )
if( is_object( $value ) ) {
if( $value instanceof json_serialize )
$result[$key] = $value->json_encode( false );
else
trigger_error( 'Object not encoded: ' . get_class( $this ).'::'.$key, E_USER_WARNING );
} else
$result[$key] = $value;
return( $asJson ? json_encode( $result ) : $result );
}
// json_encode
public function json_decode( $value ) {
$json = json_decode( $value, true );
foreach( $json as $key => $value ) {
// recursively loop through each variable reset them
}
}
}
$test = new test();
$test->obj = new test();
echo $test->string;
echo $test->json_encode();
Class PropertyExample {
private $m_value;
public function Value() {
$args = func_get_args();
return $this->getSet($this->m_value, $args);
}
protected function _getSet(&$property, $args){
switch (sizeOf($args)){
case 0:
return $property;
case 1:
$property = $args[0];
break;
default:
$backtrace = debug_backtrace();
throw new Exception($backtrace[2]['function'] . ' accepts either 0 or 1 parameters');
}
}
}
这是我处理属性获取/设置的方式,如果你想让Value()只读......那么你只需要将其改为以下内容:
return $this->m_value;
现在,函数Value()要么获取数据,要么设置数据。
readonly
在 PHP8.1 中被正式添加,可以像 @shadowhand 建议的那样使用,甚至可以在属性的构造函数样式定义中使用 - 请参见下面的 https://dev59.com/l3RC5IYBdhLWcg3wG9bp#68376398。 - jave.web