drsc10888 2011-06-13 06:19
浏览 80
已采纳

Readonly多维数组属性,PHP

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}]
";
    }
    public function __set($key, $value){
        echo "[WRITE:{$key}={$value}]
";
    }
}

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

}
  • 写回答

2条回答 默认 最新

  • doufu1504 2011-06-13 06:31
    关注

    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 the offsetSet() method:

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

    You can then initialize your ReadOnlyArray with your original array:

    $readOnlyArray = new ReadOnlyArray(array('foo', 'bar'));
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。