12

尝试更改它时,抛出异常。

4

7 回答 7

24

我想对于类属性的解决方案是:

  • 不使用您感兴趣的名称定义属性
  • 使用魔术__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
于 2010-02-26T18:16:10.530 回答
15
class test {
   const CANT_CHANGE_ME = 1;
}

你把它称为test::CANT_CHANGE_ME

于 2010-02-26T18:13:35.883 回答
1

使用常数。关键词const

于 2010-02-26T18:15:52.147 回答
1

简短的回答是您不能在 PHP 中创建只读对象成员变量。

事实上,大多数面向对象的语言都认为公开公开成员变量是一种糟糕的形式……(C# 是其属性结构的大而丑陋的例外)。

如果您想要一个类变量,请使用const关键字:

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
于 2018-11-04T21:01:47.960 回答
1

我也使用一个特征制作了一个版本。

尽管在这种情况下,该属性仍然可以由其声明类设置。

声明一个类,如:

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上查看源代码和测试

于 2020-06-27T19:14:25.003 回答
1

我制作了另一个@readonly在 docblock 中使用的版本,而不是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上查看源代码和测试

于 2021-03-11T17:42:56.473 回答
0

我知道这是一个老问题,但 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;
        }
    }
}
于 2016-09-22T21:06:40.947 回答