只读多维数组属性,PHP
我已经迷恋 ArrayAccess 和 PHP 的魔法(__get
、__set
)有一段时间了,我'我卡住了。
我正在尝试实现一个类,其中某些属性(数组)是只读的。它们最初将由构造函数设置,但此后不应修改。
通过引用使用 __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]);
}
}
I've been fooling with ArrayAccess
and PHP's magic (__get
, __set
) for awhile now, and I'm stuck.
I'm trying to implement a class in which some properties, which are arrays, are read only. They will be set initially by the constructor, but should not be modifiable thereafter.
Using __get
magic by reference, I can access array elements arbitrarily deep in the properties, and I was thinking I can throw exceptions when those properties are targeted via __set
.
The problem is though, when I'm accessing the value of an array element, PHP is calling __get
to return that part of the array by reference, and I have no knowledge of whether or not its a read or write action.
(The worst part is I knew this going in, but have been fooling with ArrayAccess
as a possible workaround solution, given the properties were instances of an implemented object)
Simple example:
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';
And the output:
[READ:foo]
[WRITE:foo=bar]
[READ:foo]
[READ:foo] // here's the problem
Realistically, I only need the value foo
(as per my example) anyways, but I need to know it's a write action, not read.
I've already half accepted that this cannot be achieved, but I'm still hopeful. Does anyone have any idea how what I'm looking to accomplish can be done?
I was considering some possible workarounds with ArrayAccess
, but so far as I can tell, I'll end up back at this spot, given I'm going to use the property notation that invokes __get
.
Update: Another fun day with ArrayAccess
.
(This is a different issue, but I suppose it works in. Posting just for kicks.)
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]);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您需要使用第二个类(实现
ArrayAccess
)来代替数组。然后,您将能够使用offsetSet()
方法控制添加到数组中的内容:然后您可以使用原始数组初始化
ReadOnlyArray
:You need to use a second class, implementing
ArrayAccess
, to use instead of your arrays. Then you will be able to control what is added to the array with theoffsetSet()
method:You can then initialize your
ReadOnlyArray
with your original array:您无法通过 ref 返回,这将解决可更改性问题,但不允许更改某些允许更改的值。
或者,您也需要将每个返回的数组包装在 ArrayAccess 中 - 并禁止在那里进行写访问。
You could not return by ref, which would solve the problem of changability, but would not allow changing of some values that are allowed to be changed.
Alternatively you need to wrap every returned array in ArrayAccess, too - and forbid write access there.