2011-06-13 93 views
2

我一直在欺骗与ArrayAccess和PHP的魔术(__get__set)一段时间了,和我被困。只读多维数组属性,PHP

我想实现一个类,其中一些属性,它是数组,只读。它们最初将由构造函数设置,但此后不应修改。

通过引用使用__get魔术,我可以访问属性中任意深度的数组元素,并且我认为当这些属性通过__set定位时,我可以抛出异常。

问题是,当我访问一个数组元素的值时,PHP正在调用__get来通过引用返回该数组的部分,并且我不知道它是否具有读或写操作。

最糟糕的是我知道这将在,但已经与ArrayAccess作为可能的解决方法解决嘴硬,给出的性能是一个实现对象的实例)

简单的例子:

class Test{ 
    public function &__get($key){ 
     echo "[READ:{$key}]\n"; 
    } 
    public function __set($key, $value){ 
     echo "[WRITE:{$key}={$value}]\n"; 
    } 
} 

$test = new Test; 

$test->foo; 
$test->foo = 'bar'; 

$test->foo['bar']; 
$test->foo['bar'] = 'zip'; 

和输出:

[READ:foo] 
[WRITE:foo=bar] 
[READ:foo] 
[READ:foo] // here's the problem 

实际上,我只需要值foo,按照我的示例),但我需要知道这是一个写操作,而不是读取。

我已经接受了一半,这是无法实现的,但我仍然充满希望。有没有人知道我想要完成的工作?

我正在考虑与ArrayAccess一些可能的解决方法,但据我所知,由于我要使用调用__get的属性表示法,因此我最终会回到这个位置。


更新:另一个有趣的一天ArrayAccess

这是一个不同的问题,但我想它在。发帖只是踢。

class Mf_Params implements ArrayAccess{ 

    private $_key  = null; 
    private $_parent = null; 
    private $_data  = array(); 
    private $_temp  = array(); 

    public function __construct(Array $data = array(), $key = null, self $parent = null){ 
     $this->_parent = $parent; 
     $this->_key  = $key; 
     foreach($data as $key => $value){ 
      $this->_data[$key] = is_array($value) 
       ? new self($value, $key, $this) 
       : $value; 
     } 
    } 

    public function toArray(){ 
     $array = array(); 
     foreach($this->_data as $key => $value){ 
      $array[$key] = $value instanceof self 
       ? $value->toArray() 
       : $value; 
     } 
     return $array; 
    } 

    public function offsetGet($offset){ 
     if(isset($this->_data[$offset])){ 
      return $this->_data[$offset]; 
     } 
     // if offset not exist return temp instance 
     return $this->_temp[$offset] = new self(array(), $offset, $this); 
    } 

    public function offsetSet($offset, $value){ 
     $child = $this; 
     // copy temp instances to data after array reference chain 
     while(!is_null($parent = $child->_parent) && $parent->_temp[$child->_key] === $child){ 
      $parent->_data[$child->_key] = $parent->_temp[$child->_key]; 
      $child = $parent; 
     } 
     // drop temp 
     foreach($child->_temp as &$temp){ 
      unset($temp); 
     } 
     if(is_null($offset)){ 
      $this->_data[] = is_array($value) 
       ? new self($value, null, $this) 
       : $value; 
     }else{ 
      $this->_data[$offset] = is_array($value) 
       ? new self($value, $offset, $this) 
       : $value; 
     } 
    } 

    public function offsetExists($offset){ 
     return isset($this->_data[$offset]); 
    } 

    public function offsetUnset($offset){ 
     unset($this->_data[$offset]); 
    } 

} 

回答

3

你需要使用第二个类,实现ArrayAccess,而不是你的数组。然后,你就可以控制什么被添加到阵列的offsetSet()方法:

class ReadOnlyArray implements ArrayAccess { 
    private $container = array(); 
    public function __construct(array $array) { 
     $this->container = $array; 
    } 
    public function offsetSet($offset, $value) { 
     throw new Exception('Read-only'); 
    } 
    public function offsetExists($offset) { 
     return isset($this->container[$offset]); 
    } 
    public function offsetUnset($offset) { 
     unset($this->container[$offset]); 
    } 
    public function offsetGet($offset) { 
     if (! array_key_exists($offset, $this->container)) { 
      throw new Exception('Undefined offset'); 
     } 
     return $this->container[$offset]; 
    } 
} 

然后,您可以您ReadOnlyArray与你原来的数组初始化:

$readOnlyArray = new ReadOnlyArray(array('foo', 'bar')); 
+0

谢谢** Benjamin Morel **;正如我提到** cweiske **,我想;我开始认为PHP对象引擎需要被审查;像这样的实例,以及缺少对'&offsetGet()'的支持都非常令人沮丧。 – Dan 2011-06-13 06:38:37

+0

感谢您的更新;我赞赏代码建议。正如我在** cweiske **的评论中所提到的,我也一直在'ArrayAccess'上挣扎,但出于不同的原因。我放弃了对ArrayAccess失败的尝试,但检查我的问题更新以查看它。 – Dan 2011-06-13 06:43:04

+0

不太清楚如果你不告诉我们你对它的期望是什么,什么不工作,你的实现有什么问题: – Benjamin 2011-06-13 06:45:54

2

你不能由参返回,这将解决changability的问题,但不允许更改允许更改的某些值。

此外,您还需要将ArrayAccess中的每个返回数组都包装起来 - 并且禁止在那里写入访问。

+1

由于* cweiske *;我有点想到这一点。我也一直在苦于'ArrayAccess'。我已经成为最高错误报告级别(*'error_reporting(-1);'*)的粉丝,并且一直在与PHP斗争,并在访问不存在的数组索引时停止为单个类类型发出通知/写入。我想我可能会把责任留给客户程序员,不要修改这些值。 – Dan 2011-06-13 06:37:14