PHP中如何实现只读成员变量?

发布于 2024-08-23 06:45:16 字数 20 浏览 6 评论 0原文

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

When trying to change it,throw an exception.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

桃气十足 2024-08-30 06:45:16

我想对于类属性,一个解决方案是:

  • 不定义一个您感兴趣的名称的属性,
  • 使用神奇的 __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

I suppose a solution, for class properties, would be to :

  • not define a property with the name that interests you
  • use the magic __get method to access that property, using the "fake" name
  • define the __set method so it throws an exception when trying to set that property.
  • See Overloading, for more informations on magic methods.

For variables, I don't think it's possible to have a read-only variable for which PHP will throw an exception when you're trying to write to it.

For instance, consider this little class :

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
        }
    }
}

Instanciating the class and trying to read the property :

$a = new MyClass();
echo $a->myVar . '<br />';

Will get you the expected output :

test

While trying to write to the property :

$a->myVar = 10;

Will get you an Exception :

Exception: not allowed : myVar in /.../temp.php on line 19
黒涩兲箜 2024-08-30 06:45:16
class test {
   const CANT_CHANGE_ME = 1;
}

您将其称为 test::CANT_CHANGE_ME

class test {
   const CANT_CHANGE_ME = 1;
}

and you refer it as test::CANT_CHANGE_ME

尹雨沫 2024-08-30 06:45:16

使用常数。关键字const

Use a constant. Keyword const

心在旅行 2024-08-30 06:45:16

简而言之,您无法在 PHP 中创建只读对象成员变量。

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

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

class MyClass {
    public const myVariable = 'x';
}

可以访问该变量:

echo MyClass::myVariable;

无论您有多少个 MyClass 类型的不同对象,该变量都将仅存在于一个版本中create,在大多数面向对象的场景中它几乎没有用处。

但是,如果您想要一个每个对象可以有不同值的只读变量,则应该使用私有成员变量和访问器方法(也称为 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

The short answer is you can't create a read-only object member variable in PHP.

In fact, most object-oriented languages consider it poor form to expose member variables publicly anyway... (C# being the big, ugly exception with its property-constructs).

If you want a class variable, use the const keyword:

class MyClass {
    public const myVariable = 'x';
}

This variable can be accessed:

echo MyClass::myVariable;

This variable will exist in exactly one version regardless of how many different objects of type MyClass you create, and in most object-oriented scenarios it has little to no use.

If, however, you want a read-only variable that can have different values per object, you should use a private member variable and an accessor method (a k a getter):

class MyClass {
    private $myVariable;
    public function getMyVariable() {
        return $this->myVariable;
    }
    public function __construct($myVar) {
        $this->myVariable = $myVar;
    }
}

The variable is set in the constructor, and it's being made read-only by not having a setter. But each instance of MyClass can have its own value for 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
过潦 2024-08-30 06:45:16

我制作了另一个版本,在文档块中使用 @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).'::\

请参阅源代码&在 我的 gitlab 上进行测试

.$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 上进行测试

I made another version that uses @readonly in the docblock instead of private $r_propname. This still doesn't stop the declaring class from setting the property, but will work for public readonly access.

Sample Class:

class Person {
    use Readonly;

    /**
     * @readonly
     */
    protected $name;

    protected $phoneNumber;

    public function __construct($name){
        $this->name = $name;
        $this->phoneNumber = '123-555-1234';
    }
}

The ReadOnly trait

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).'::\

See the source code & test on my gitlab

.$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); } }

See the source code & test on my gitlab

花海 2024-08-30 06:45:16

我也使用一个特征编写了一个版本。

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

声明一个类,如下所示:

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).'::\

请参阅源代码&在 我的 gitlab 上进行测试

.$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 上进行测试

I cooked up a version, too, using a trait.

Though in this case, the property can still be set by its declaring class.

Declare a class like:

class Person {
    use Readonly;

    protected $name;
    //simply declaring this means "the 'name' property can be read by anyone"
    private $r_name;
}

And this is the trait I made:

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).'::\

See the source code & test on my gitlab

.$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); } }

See the source code & test on my gitlab

扛刀软妹 2024-08-30 06:45:16

我知道这是一个老问题,但帕斯卡的回答确实对我有帮助,我想补充一点。

__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;
        }
    }
}

I know this is an old question, but PASCAL's answer really helped me and I wanted to add to it a bit.

__get() fires not only on nonexistent properties, but "inaccessible" ones as well, e.g. protected ones. This makes it easy to make read-only properties!

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;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文