2011-06-13 89 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