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 = 'bar';

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

And the output:

[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){
            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){
            $this->_data[] = is_array($value)
                ? new self($value, null, $this)
                : $value;
            $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){



您需要使用第二个类来实现 ArrayAccess </ code>,而不是使用数组。 然后,您将能够使用 offsetSet()</ code>方法控制添加到数组的内容:</ p>

 类ReadOnlyArray实现ArrayAccess {
private $ container = array();
public function __construct(array $ array){
$ this-&gt; container = $ array;
public function offsetSet($ offset,$ value){
throw new Exception ('只读');
公共函数offsetExists($ offset){
return isset($ this-&gt; container [$ offset]);
公共函数offsetUnset($ offset) {
unset($ this-&gt; container [$ offset]);
公共函数offsetGet($ offset){
if(!array_key_exists($ offset,$ this-&gt; container)){\ n throw new Exception('Undefined offset');
返回$ this-&gt; container [$ offset];
</ code> </ pre>

然后,您可以使用原始数组初始化 ReadOnlyArray </ code>:</ p>

  $ readOnlyArray = new ReadOnlyArray(array('foo','bar')  ); 
</ code> </ pre>
</ div>



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) {
    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'));

dtx9763 如果您的意图是在访问不存在的偏移量时不报告错误,则只返回null; 而不是抛出异常!
9 年多之前 回复
dongzhao1865 莫雷尔:我发布了一大堆代码; 虽然我这样做只是为了踢,但我想知道 - 我正在努力实现这一点吗? 在您提供的答案中,当偏移量不存在时抛出异常。 我在一个具有最高错误报告(-1)的环境中工作,当然,每当我指向不存在的数组索引时,通知都会被抛出。 我正试图克服这个问题,因为这个数据结构的意图是松散的,没有错误报告所施加的严格限制。 有任何想法吗?
9 年多之前 回复
douguai6716 真正。 (我正在编辑它,它或多或少不相关,只是想我会分享)它几乎按预期工作; 当访问不存在的数组索引或写入包含数组(给定嵌套元素)时,不会抛出通知。 问题是,isset总是返回true,并且递增一个不存在的数组索引会将临时对象强制转换为int(抛出错误)我希望PHP很快就会添加对__toInt,__ toArray等的支持。我不能做多少关于那个 现在。
9 年多之前 回复
douzi9744 如果您没有告诉我们您对它的期望以及哪些不起作用,那么您的实施有什么问题并不十分清楚:)
9 年多之前 回复
douyeyan0650 感谢您的更新; 我很欣赏代码建议。 正如我在对cweiske的评论中提到的那样,我也一直在努力使用ArrayAccess,但出于不同的原因。 我放弃了对ArrayAccess惨败的尝试,但请检查我的问题更新以查看它。
9 年多之前 回复
doufangxie0203 谢谢Benjamin Morel; 正如我提到的cweiske,我想; 我开始认为需要审查PHP对象引擎; 像这样的实例,以及缺乏对&offsetGet()的支持是非常令人沮丧的。
9 年多之前 回复

你无法通过ref返回,这将解决可变性问题,但不允许更改允许的某些值 更改。</ p>

或者你也需要在ArrayAccess中包装每个返回的数组 - 并且禁止在那里进行写访问。</ p>
</ div>



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.

doutao5499 谢谢cweiske; 我有点想通了。 我也一直在努力使用ArrayAccess。 我已成为最高错误报告级别(error_reporting(-1);)的粉丝,并且在访问/写入不存在的数组索引时,一直在与PHP斗争,以停止为单个类型类型抛出通知。 我想我可能只是责任客户程序员不要修改值。
9 年多之前 回复
Csdn user default icon