尝试更改时,抛出异常。
尝试更改时,抛出异常。
针对类的属性,我想一个解决方案是:
__get
访问该属性,使用“虚拟”名称__set
方法,以便在尝试设置该属性时抛出异常。对于变量,我认为不可能拥有只读变量,当您尝试写入它时,PHP会引发异常。
例如,考虑这个小类:
class MyClass {
protected $_data = array(
'myVar' => 'test'
);
public function __get($name) {
if (isset($this->_data[$name])) {
return $this->_data[$name];
} else {
// non-existant property
// => up to you to decide what to do
}
}
public function __set($name, $value) {
if ($name === 'myVar') {
throw new Exception("not allowed : $name");
} else {
// => up to you to decide what to do
}
}
}
实例化类并尝试读取属性:
$a = new MyClass();
echo $a->myVar . '<br />';
以下代码将会得到你期望的输出:
test
在尝试写入该属性时:
$a->myVar = 10;
使用这段代码将会抛出一个异常:
Exception: not allowed : myVar in /.../temp.php on line 19
__call
有什么用? - user198729__get
пјүпјҢе°Ҷи°ғз”Ё__call
--иҜ·еҸӮи§Ғhttp://fr2.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methodsгҖӮ - Pascal MARTINpublic function modify() { $this->_data['myVar'] = 'bla'; }
。或者只需扩展 MyClass
并重载 __set()
方法。它与常规受保护的值一样只读 - 这完全取决于类方法的实现。 - chelmertzclass test {
const CANT_CHANGE_ME = 1;
}
你将其称为test::CANT_CHANGE_ME
test
实例,其中包含CANT_CHANGE_ME
?这是一个类变量,而不是成员变量,它只存在于一个副本中... - Erk使用一个常量。关键字const
@readonly
代替private $r_propname
。这仍然无法阻止声明类设置属性,但对于公共只读访问将起作用。class Person {
use Readonly;
/**
* @readonly
*/
protected $name;
protected $phoneNumber;
public function __construct($name){
$this->name = $name;
$this->phoneNumber = '123-555-1234';
}
}
只读特质 ReadOnly
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$refProp = new \ReflectionProperty($this, $prop);
$docblock = $refProp->getDocComment();
// a * followed by any number of spaces, followed by @readonly
$allow_read = preg_match('/\*\s*\@readonly/', $docblock);
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
查看源代码并测试,请前往我的GitLab
我也使用了一个特质来编写一个版本。
虽然在这种情况下,属性仍然可以由其声明类设置。
声明一个类如下:
class Person {
use Readonly;
protected $name;
//simply declaring this means "the 'name' property can be read by anyone"
private $r_name;
}
这是我创建的特征:
trait Readonly {
public function readonly_getProperty($prop){
if (!property_exists($this,$prop)){
//pretty close to the standard error if a protected property is accessed from a public scope
// throw new \Error("Property '{$prop}' on class '".get_class($this)."' does not exist");
trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
}
$allow_read = property_exists($this, 'r_'.$prop );
if ($allow_read){
$actual = $this->$prop;
return $actual;
}
throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
}
public function __get($prop){
return $this->readonly_getProperty($prop);
}
}
查看源代码并在我的GitLab上进行测试
class MyClass {
public const myVariable = 'x';
}
这个变量可以被访问:
echo MyClass::myVariable;
MyClass
对象,此变量仅存在于一个版本中,在大多数面向对象的场景中,它几乎没有用处。如果您想要一个只读变量,可以针对每个对象使用不同的值,则应使用私有成员变量和访问器方法(也称为getter):class MyClass {
private $myVariable;
public function getMyVariable() {
return $this->myVariable;
}
public function __construct($myVar) {
$this->myVariable = $myVar;
}
}
MyClass
的每个实例可以拥有自己的myVariable
值。$a = new MyClass(1);
$b = new MyClass(2);
echo $a->getMyVariable(); // 1
echo $b->getMyVariable(); // 2
$a->setMyVariable(3); // causes an error - the method doesn't exist
$a->myVariable = 3; // also error - the variable is private
我知道这是一个老问题,但PASCAL的答案确实帮了我很多,我想再补充一点。
__get()不仅在不存在的属性上触发,还会在“无法访问”的属性上触发,例如受保护的属性。这使得创建只读属性变得非常容易!
class MyClass {
protected $this;
protected $that;
protected $theOther;
public function __get( $name ) {
if ( isset( $this->$name ) ) {
return $this->$name;
} else {
throw new Exception( "Call to nonexistent '$name' property of MyClass class" );
return false;
}
}
public function __set( $name ) {
if ( isset( $this->$name ) ) {
throw new Exception( "Tried to set nonexistent '$name' property of MyClass class" );
return false;
} else {
throw new Exception( "Tried to set read-only '$name' property of MyClass class" );
return false;
}
}
}