【问题标题】:Cannot set read-only property无法设置只读属性
【发布时间】:2012-02-16 16:02:41
【问题描述】:

PHP 中是否真的存在只读属性而我不知道它们?如何将公共属性设为只读??

我只是在玩 ReflectionClass,尝试覆盖属性时收到以下错误消息:

$lol = new ReflectionObject($obj);
$lol->name = 'awawawawa';

【问题讨论】:

  • 呃!属性不是这样工作的:它们要么是私有的,要么是受保护的,要么是公共的……尽管您可以使用魔术 getter 和 setter 来模拟它
  • 我知道,我正在使用 getter 来达到这个目的 :( 但是 ReflectionClass 用什么来使它们只读???显然它们不是 getter,因为它们不会出现在那里.. .
  • 那么 ReflectionObject 不是 $obj 的另一个实例,也不是指向它的指针,所以它没有相同的方法……它是一个具有自己独特属性和方法的 ReflectionObject可用于显示有关 $obj 的信息。 ReflectionObject 没有名为“name”的属性(公共或其他),即使 $obj 有,因此您无法更改该值。我假设 Reflection 以自己的方式实现了魔法 getter/setter 以防止这种情况发生
  • @MarkBaker 实际上,它有一个名为 name 的公共属性。它被记录在案。 ReflectionObject extends ReflectionClassReflectionClass 包含 public $ReflectionClass->name;

标签: php properties readonly


【解决方案1】:

来自反射类的代码:

/* {{{ _reflection_write_property */
static void _reflection_write_property(zval *object, zval *member, zval *value TSRMLS_DC)
{
    if ((Z_TYPE_P(member) == IS_STRING)
        && zend_hash_exists(&Z_OBJCE_P(object)->default_properties, Z_STRVAL_P(member), Z_STRLEN_P(member)+1)
        && ((Z_STRLEN_P(member) == sizeof("name") - 1  && !memcmp(Z_STRVAL_P(member), "name",  sizeof("name")))
            || (Z_STRLEN_P(member) == sizeof("class") - 1 && !memcmp(Z_STRVAL_P(member), "class", sizeof("class")))))
    {
        zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, 
            "Cannot set read-only property %s::$%s", Z_OBJCE_P(object)->name, Z_STRVAL_P(member));
    }
    else
    {
        zend_std_obj_handlers->write_property(object, member, value TSRMLS_CC);     
    }
}
/* }}} */

所以基本上,它明确禁止“名称”和“类”属性。 我找不到任何迹象表明存在类属性!

【讨论】:

    【解决方案2】:

    来自 ReflectionObject 的 PHP 手册页:

    Properties
    
    name
    
        Name of the object's class. Read-only, throws ReflectionException in attempt to write.
    

    http://sk.php.net/manual/en/class.reflectionobject.php

    没有太多关于如何他们这样做,但我的猜测是它明确地寻找 name 属性的写入并阻止它发生,因为它会使反射成为撒谎。

    你可以自己做类似的事情:

    <?php
    class MyReadOnlyJunk
    {
        protected // over private, or not defined here at all
            $name = 'My Name';
    
        public function __set($key, $val)
        {
            if($key == 'name')
                throw new Exception('Cannot has name set!');
        }
    }
    ?>
    

    【讨论】:

    • 我认为这行不通,因为属性存在所以 __set 永远不会触发...
    • 是的,对不起,如果它不公开或不存在,它就可以工作(尽管成员方法可能能够设置它,除非它不存在)
    【解决方案3】:

    PHP docs:

    对象类的名称。只读,尝试写入时抛出 ReflectionException。

    我在Properties 页面中没有看到任何关于将内容设置为只读的内容,不过……我想可能会在它们前面加上final,但我不知道这是否允许,因为它只在方法。

    【讨论】:

      【解决方案4】:

      来自ReflectionClass的文档:

      属性
      名称
      类的名称。只读,尝试写入时抛出 ReflectionException。

      但是,文档也说

      ReflectionClass implements Reflector {
        /* Properties */
        public $ReflectionClass->name;
        ...
      

      这里需要注意的是,(即使文档看起来 ReflectionClass 是用纯 PHP 实现的)ReflectionClass 是 PHP 核心的一部分,因此用 C 实现!

      虽然该属性被记录为普通的公共属性,但实际上并非如此!

      我懒得为此深入研究 PHP 源代码,但你会发现那里有一个特殊情况处理,它保护公共属性使其成为只读。编辑:见 Mark Ba​​kers 的回答。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-09-29
        • 1970-01-01
        • 2017-03-24
        • 2020-01-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多