15

I wrote a simple collection class so that I can store my arrays in objects:

class App_Collection implements ArrayAccess, IteratorAggregate, Countable
{
    public $data = array();

    public function count()
    {
        return count($this->data);
    }

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

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

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

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

    public function getIterator()
    {
        return new ArrayIterator($this->data);
    }
}

Problem: when calling array_key_exists() on this object, it always returns "false" as it seems this function is not being handled by the SPL. Is there any way around this?

Proof of concept:

$collection = new App_Collection();
$collection['foo'] = 'bar';
// EXPECTED return value: bool(true) 
// REAL return value: bool(false) 
var_dump(array_key_exists('foo', $collection));
tereško
  • 58,060
  • 25
  • 98
  • 150
Aron Rotteveel
  • 81,193
  • 17
  • 104
  • 128
  • array_key_exists does not work with *ArrayAccess* but it does w/ *ArrayObject*, see: https://3v4l.org/gYeCM – hakre Aug 19 '18 at 13:25

1 Answers1

25

This is a known issue which might be addressed in PHP6. Until then, use isset() or ArrayAccess::offsetExists().

Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • 1
    Thanks for the answer; could you provide a reference with that? – Aron Rotteveel Oct 08 '09 at 14:25
  • 4
    I don't believe this is written anywhere in the manual, because it wasn't a design choice. It was a design oversight, a bug I might say. There are many inconsistencies in the PHP language. I've proposed this functionality on the PHP internals mailing list, and people agreed with me, but it will going to be a long time until it will get implemented. I don't know C unfortunately. – Ionuț G. Stan Oct 08 '09 at 14:29
  • 2
    Here's a link to that discussion: http://marc.info/?l=php-internals&m=122483924802616&w=2 – Ionuț G. Stan Oct 08 '09 at 14:32
  • 2
    Thanks for the comments. Diving intro SPL makes me realize more and more how inconsistent the language is. For now, I'll just isset(). – Aron Rotteveel Oct 08 '09 at 14:34
  • I know this is an old thread, but is there any update around this, any workarounds available? I am trying to refactor in an application where switching to isset is not an option. Stuff like this makes one get angry at php, not which order array functions take parameters.. – Ms01 May 05 '14 at 13:29
  • 2
    @cubsink seems to be working as desired in PHP 5.4.24. – Ionuț G. Stan May 05 '14 at 13:45
  • @IonuțG.Stan and as desired means.. calling offsetExists? – Ms01 May 05 '14 at 13:48
  • @cubsink I meant this: https://gist.github.com/igstan/e7d3458260f67895dacb Are you dealing with a different issue? – Ionuț G. Stan May 05 '14 at 13:57
  • I think this is the same issue. I have a class where I have implemented the ArrayAccess (and extending ArrayObject) but when calling array_key_exists('foo', $myobj) it always returns false. I just thought it would call offsetExists on my class. Sorry for the confusion, and thanks for the super quick answers :) – Ms01 May 05 '14 at 14:04
  • @cubsink you're right, it doesn't call `offsetExists`. My gist looks like it's working because casting an `ArrayObject` to an array constructs the expected array, and `array_key_exists` seems to perform an internal cast. Maybe you can redefine `array_key_exists` in another namespace? https://gist.github.com/igstan/87154e85ffc5f2fe30e3 – Ionuț G. Stan May 05 '14 at 14:34
  • @IonuțG.Stan woah, never thought of that as a possibility. Now I just have to talk over my tech lead that we should pursue this option instead of the ugly hack I did earlier today to solve this. – Ms01 May 05 '14 at 19:54
  • It's worth noting that `isset` and `array_key_exists` behave differently: `isset` will return `false` if it encounters a `null` (but existing) value, while `array_key_exists` will return `true`. In order to account for this and maintain compatibility with `isset`, you would need to create an additional method to check whether a key exists, since `offsetExists` is (perhaps erroneously) used by `isset`. In theory, `isset` should call `offsetGet` if `offsetExists` returns `true`, but, in practice, I believe this does not happen as of PHP 5.5. – Zenexer Jul 11 '14 at 19:11
  • 4
    php 7.2.0alpha2 behaves the same way – Konstantin Yaniv Jul 08 '17 at 10:45
  • 1
    Aand, 10 years later since the question and 2 years since the last comment, this thing is still claiming hours of debugging off the unsavvy. I know this comment is slightly off-key, I'll take the blame for it. – Dinu Aug 16 '19 at 20:17
  • Still a problem, it seems. :( – Olle Härstedt Aug 05 '22 at 10:48