Web:Extend

Blog » ArrayAccess Quirks

Recently we stumbled upon a bug in our framework. The problem was tricky. We were submitting values to be inserted in an SQL query and some of them weren’t found by the code that actually build the query with all the escaping needed automatically. Why’s that? Because we were passing an ArrayAccess object containing the values instead of a normal array.

The Problem

It is written deep in the PHP documentation that objects that implement the interface ArrayAccess do not work with the array_* functions. None of them. Not even array_key_exists. You would usually use this function to check if a key exists in the array, even if the associated value is null. But you can’t. Some people wrote bug reports to the PHP team about it. They were marked as bogus.

Of course, no error or warning is triggered when you use it with this function. It would be too easy. And this can leads to mistake, like the bug we found in our code.

So what can we use if array_key_exists doesn’t work?

The Solution

Instead of array_key_exists we can use the isset function. isset, used on an ArrayAccess object, will call its method offsetExists. This method returns a boolean: whether the offset exists or not.

If we implement offsetExists this way, we can use isset to check if an array has a key, including the ones associated with null values:

// $this->aRow is a real array stored as a protected property of the object

public function offsetExists($offset)
{
	return array_key_exists($offset, $this->aRow);
}

Using isset on an ArrayAccess object that implements this function will give the same result as using array_key_exists directly on the object. Example:

$o = new myArrayAccessClass;
$a = array();

// isset, used on an ArrayAccess object...
echo (int)isset($o['key']);
// ... is the same as array_key_exists used on a real array
echo (int)array_key_exists('key', $a);

We also added an unit test about this in our framework. Never knows, maybe array_key_exists is going to accept ArrayAccess objects in the future.

The Ugly

Well, I’m sure you can begin to see the ugly in this: isset giving the same results as array_key_exists on ArrayAccess objects, but different results on real arrays. That means needing to pay more attention to what you actually use these functions on.

Another ugly result is that you will need to do 2 tests when you accept both arrays and objects. If you received an array you’ll have to use array_key_exists, otherwise isset. This results in the following conditional statement:

if ((is_array($m) && array_key_exists($sKey, $m)) || (!is_array($m) && isset($m[$sKey])))
	; // insert code here...

Take care about these issues next time you use ArrayAccess!

Thanks for reading.

March 1st, 2008 by Loïc Hoguin · Tags: PHP5, Tips, Web:Extend

% Comment

  1. David Goodwin Says:

    Thanks for writing – I’ve just discovered this after thinking I was clever in how I was lazy sanitising data (by proxying it in an ArrayAccess implementing object).

    *sigh*

    Guess I’ll have to do a recursive clean afterall….

© 2008 Loïc Hoguin (essen), Anthony Ramine (nox)