php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #51622 ArrayObject::offsetGet should return a reference
Submitted: 2010-04-21 13:35 UTC Modified: 2010-04-27 10:02 UTC
Votes:12
Avg. Score:4.5 ± 0.8
Reproduced:11 of 11 (100.0%)
Same Version:5 (45.5%)
Same OS:3 (27.3%)
From: oliver dot graetz at gmx dot de Assigned:
Status: Open Package: SPL related
PHP Version: 5.2.13 OS:
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2010-04-21 13:35 UTC] oliver dot graetz at gmx dot de
Description:
------------
This bug refers to my report filed under http://bugs.php.net/bug.php?id=34783 which is now more than four years old. In the meantime I found out that using ArrayObject instead of the test class the

    $t['huba'][]='three';

actually works, thanks to the SPL using its "implemented in C advantage" to circumvent the problem. Actually, it works until the programmer decides to inherit from ArrayObject and overwrite offsetGet(). Then the problem of the offsetGet() method not returning by reference is back.

Back in 2005 you were very quick to flag the report as BOGUS, but a look at the source code of "zend_interfaces.c" proves that there is in fact a problem:

ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_get, 0, 0, 1) /* actually this should be return by ref but atm cannot be */

The best way of dealing with this is not to mark it as BOGUS and deny that there is a problem. It would be admitting the fault and perhaps introducing an alternative NewArrayAccess interface that defines &offsetGet(). So future code can use it without breaking old implementations.


Test script:
---------------
<?php
class Test1 extends ArrayObject
{
}
class Test2 extends ArrayObject
{
	function offsetGet($key) { return parent::offsetGet($key); }
}

$t1           = new Test1();
$t1['huba']   = array('one','two');
$t1['huba'][] = 'three';
print_r($t1);

$t2           = new Test2();
$t2['huba']   = array('one','two');
$t2['huba'][] = 'three';
print_r($t2);


Expected result:
----------------
Test1 Object
(
    [huba] => Array
        (
            [0] => one
            [1] => two
            [2] => three
        )

)

Test2 Object
(
    [huba] => Array
        (
            [0] => one
            [1] => two
            [2] => three
        )

)

Actual result:
--------------
Test1 Object
(
    [huba] => Array
        (
            [0] => one
            [1] => two
            [2] => three
        )

)

Notice: Indirect modification of overloaded element of Test2 has no effect in F:\huba.php on line 17
Test2 Object
(
    [huba] => Array
        (
            [0] => one
            [1] => two
        )

)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-04-27 10:02 UTC] colder@php.net
-Summary: ArrayObject shows inconsistent behaviour +Summary: ArrayObject::offsetGet should return a reference -Type: Bug +Type: Feature/Change Request
 [2010-04-27 10:02 UTC] colder@php.net
This is actually a feature request. To be backward compatible, we would need to 
have a new interface, and obviously a new interface only to solve that reference 
thing is painful.

The immediate solution is to return an ArrayObject itself, which means that 
indirect modifications will work.
 [2010-06-03 23:32 UTC] andrewm dot finewolf at gmail dot com
Arrays in PHP contains references to primitive types and reference types. Which basically means that if you are post-incrementing an element, well, it actually works.

Why is ArrayAccess::offsetGet() returns by value instead of by reference? Wasn't ArrayAccess created to emulate an array? This is a major inconsistency in the platform and makes this whole interface pretty useless. This isn't an engine limitation. __get(), __set(), __isset(), __unset() is returning values by reference without any problems. Why can't ArrayAccess (when it does pretty much the same thing?)

Can ArrayAccess::offsetGet() return by reference (or at the very least create a second interface, "ArrayAccessRef", for this)?
 [2010-08-18 10:21 UTC] golgote at mamasam dot com
I'd like to see a solution to this problem as well. I have spent countless hours 
trying to use ArrayObject and ArrayAccess and they both have problems either 
with get and unset. An unset() on an ArrayObject issues the notice :

        $object["list"][0]["prods"] = "1,3";
        $object["list"][0]["cache"][1] = array(
                        'name' => 'p3',
                        'categories' => array(
                            array('category' => 'c3'), 
                            ),
                        'price' => 3
                        );
        unset($object["list"][0]["cache"][2]);

will issue Notice: Indirect modification of overloaded element... while this 
works with ArrayAccess! 

But something like a straight :
        $object['arr'][0]['foo'] = 'bar';
will issue the notice with ArrayAccess but not with an ArrayObject!

It's really silly.

I suggest that since this bug hasn't been fixed since 2005, it should be 
documented clearly that neither ArrayAccess and ArrayObject work correctly with 
multidimensional arrays and are just quick, funny but useless hacks so that 
people stop wasting their time with them.
 [2011-02-18 23:35 UTC] urkle at outoforder dot cc
Is this issue fixed in PHP 5.3.4 which supports defining &offsetGet ? (As documented in http://php.net/manual/en/arrayaccess.offsetget.php )

Actually Just tested and it's not..  Though ArrayAccess by itself works wonderfully.
--- Test script (Tested on PHP 5.3.5 x86_64 on Fedora 14)
<?php

class Test implements ArrayAccess {
        private $data = array();

        /** Array Access */
        public function offsetExists($offset)
        {
                return isset($this->data[$offset]);
        }

        public function &offsetGet($offset)
        {
                return $this->data[$offset];
        }

        public function offsetSet($offset, $value)
        {
                $this->data[$offset] = $value;
        }

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

class Test2 extends ArrayObject {
        public function &offsetGet($offset)
        {
                $t =& parent::offsetGet($offset);
                return $t;
        }
}

$t = new Test();
$t['test'] = array(1,2,3,4);
echo serialize($t)."\n";
$t['test'][] = 5;
echo serialize($t)."\n";

$t = new Test2();
$t['test'] = array(1,2,3,4);
echo serialize($t)."\n";
$t['test'][] = 5;
echo serialize($t)."\n";
 [2013-04-16 04:54 UTC] me at achronos dot ca
Although no php dev has ever mentioned it in a bug report (that I can find), this issue has in fact been fixed.
At least, it has been fixed for ArrayAccess.
For ArrayObject, I haven't tested.

According to the PHP docs (http://php.net/manual/en/arrayaccess.offsetget.php notes section)
this was fixed in 5.3.4 by relaxing the requirements for implementing AccessAccess, so that
 public function &offsetGet($offset) {}
works, and thus indirect access works.

I've confirmed this does in fact work (on 5.3.8).

There are multiple bug reports all related to this topic:
https://bugs.php.net/bug.php?id=51622
https://bugs.php.net/bug.php?id=34783
https://bugs.php.net/bug.php?id=32983
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Sun Nov 19 01:31:42 2017 UTC