Here’s a quick question for the wider PHP programming community … if you’re writing code that tests for the presence of an array, should is_array() also accept objects that behave like arrays?

$testObject = new ArrayObject(array('Tom'));
$testResult = is_array($testObject);

On PHP 5.3.2, $testResult gets set to false. If you wanted to get an object that behaves like an array past this sort of test, you’d have to write:

$testObject = new ArrayObject(array('Tom'));
$testResult = (is_array($testObject) || $testObject instanceof ArrayAccess);

I’m just curious as to what other PHP programmers think of this. Having to write the second test is longer, and there seems to be little or no benefit gained by having to do so.

What do you think?

27 Comments

  1. Cristiano says:
    July 19th, 2010 at 9:17 pm

    No, otherwise it would break the concept of testing an array type instead of testing an object type. Those are actually 2 distinct things, so it would not make much is_array testing for an object.

  2. Evert says:
    July 19th, 2010 at 9:19 pm

    No,

    is_array checks for a type. An object implementing ArrayAccess is definitely not an array.

    One thing that could happen, is a new function that checks for either (which btw you can easily create yourself).

    My biggest peeve for ArrayAccess and Traversable is that I would love to see other php functions treat them as normal arrays. That would put them on par with ‘actual’ arrays.

    Even then though I would still not want is_array to return true, because it’s simply not a variable of type array.

  3. Dennis de Greef says:
    July 19th, 2010 at 9:45 pm

    Well, that’s a good question…

    For me, i usually use is_array() in if-statements to check wether i can loop over the given variable using array access syntax.
    In that case i would say yes, it should.

    On the other hand, you might wan’t to check wether the datatype actually is an array (can’t think of a case when you would explicitly want this over ArrayObjects)
    My guess would be to use if(gettype($arrayObject) == ‘array’) if you require the ‘array’ datatype.

  4. duo says:
    July 19th, 2010 at 9:54 pm

    I would say no. is_array accepting ArrayObject is probably dangerous. Many other array functions do not work on ArrayObject (sort, array_*…etc) also.

  5. Lukas says:
    July 19th, 2010 at 10:01 pm

    i remember there was some discussion on the internals mailinglist about making ArrayObject work with all the various array functions.

  6. Greg says:
    July 19th, 2010 at 10:05 pm

    As soon as it works with all array functions, sure. Until then, no!

  7. Josh Davis says:
    July 19th, 2010 at 10:07 pm

    I’m a supporter of strong type checking, but ArrayObject is a special case. It was designed to mask as an array, so it would make sense for it to return true on is_array() and be accepted everywhere an array is expected.

    This is assuming a vanilla ArrayObject can behave exactly like an array, which I think it does considering it implements Traversable, ArrayAccess, Serializable and Countable. I’m not sure which of those should be checked by is_array() though, perhaps all four, not just ArrayAccess.

  8. Patrick says:
    July 19th, 2010 at 10:27 pm

    I think the `is_array’ function behaves correct.

    It would be nice to have the ArrayObject as a SPL Type,
    but there is no such SPLArray, now.

    It would be nice to have all the helpfull array functions as methods of ArrayObject (and hopefully a SPLType like SPLString includes Stringmethods).

    like:

    $testObjSize = $testObject->count();
    
    $hasKey = $testObject->array_key_exists($search);
    

    which would be possible with Autoboxing (http://wiki.php.net/rfc/autoboxing).

  9. Matthew Spivey says:
    July 19th, 2010 at 10:48 pm

    I’d prefer that the array functions support more classes, like ArrayObject. This could possibly be implemented using the getIterator() function. For example, I’d like to be able to pass an ArrayObject into the array_map function.

  10. Michael Gauthier says:
    July 19th, 2010 at 11:03 pm

    You need to check for more than just ArrayAccess. Arrays are also Countable and Iterable.

  11. Josh Johnston says:
    July 19th, 2010 at 11:03 pm

    I run into the same issue quite frequently. Unfortunately making is_array return true for Array-ish objects might break existing code that has logic for objects vs arrays.

    A good solution would be to implement new wrapper functions that accept both. Maybe something like is_traversable() that would accept an array or any object that implements Traversable. The idea can then be extended to arrays and/or ArrayAccess, or even for string vs an object that implements __toString()

    Right now I use my own helper functions like is_arrayish() that does the compound check

  12. Greg says:
    July 20th, 2010 at 12:21 am

    I may be mistaken, but isn’t it still an object technically? An object that acts as an array, yes, but still an object, right? So then it should fail is_array().

    Again, if I’m wrong, just ignore me :).

  13. Ian says:
    July 20th, 2010 at 1:02 am

    I think the answer should be very easy:

    Are they interchangeable? Is a developer testing for behavior/functionality or are they testing for an object?

    Really, is_array() is used to see if you can use a variable as an Array. As such, as you mention, any element/object/etc that operates in the same way as an Array should return true.

    Cheers

  14. David says:
    July 20th, 2010 at 2:08 am

    This issue also comes into play with type hinting.
    Having an arraylike type would be really nice. That said, arrays are not objects, so you might run into issues with arraylike objects being passed by reference.

  15. Larry Garfield says:
    July 20th, 2010 at 4:51 am

    There’s so much more going on there. As Michael indicated above, arrays are more than just ArrayAccess. If one were to create an ArrayInterface (that would then satisfy is_array()), it would be a combination if Traversable (or possibly Iterator), ArrayAccess, and Countable. Plus probably a few other interfaces that don’t currently exist. And then there’s the question of pass-by-value vs. pass-by-handle (not to be confused with pass-by-reference).

    At this point, honestly what we probably want is to instead break is_array() into a series of functions:

    is_iteratable($a) { return is_array($a) || $a instanceof Traversable; }

    is_countable($a) { return is_array($a) || $a instanceof Countable; }

    is_array_accessible($a) { return is_array($a) || $a instanceof ArrayAccess; }

    and so on. Then you can check for specifically what you intend to do with it, and both array and the various quasi-array-ish objects would both pass.

    I’d be very much in favor of allowing ArrayObject to be passed to internal array functions, too, if it could be done cleanly.

  16. sokzzuka says:
    July 20th, 2010 at 6:36 am

    Better idea – change the default array implementation to the ArrayObject one, and there will be no confusion :)

  17. Tim Fountain says:
    July 20th, 2010 at 8:24 am

    I would say no, otherwise if you wanted to explicitly check for the array type how would you do it? It’d be better to add a new, looser type that matches both; in the same way that is_numeric works on the string ’1′ and the integer 1.

  18. Daniel O'Connor says:
    July 20th, 2010 at 2:39 pm

    In a perfect world, we wouldn’t have a non-object array notion :(

    - return is_array($foo)
    + return $foo implements ArrayAccess;

  19. Ryan Horn says:
    July 20th, 2010 at 3:22 pm

    It’s a tough one, my mind wants to say no unless the default array type used ArrayObject. However, there are some cases where you are forced to use ArrayObject instead of the default type, and having to remember special cases like this can cause some headaches. A good example is using overloaded getters/setters. If one of the properties is an array, it needs to be an object for direct modification of it to work, which usually results in wrapping it in ArrayObject.

  20. Dennis says:
    July 20th, 2010 at 4:54 pm

    Obviously, not. I don’t think you should ever do sort() on ArrayAccess instance. If you ever would like to sort() an ArrayAccess instance, then probably your logic is wrong. ArrayAccess should not be used to be able to get rid of plain arrays.

    is_traversable() is a good solution. Many functions should be able to accept ArrayAccess instances as input parameters, where they allow arrays.

    However, I dislike the whole idea of ArrayAccess. I don’t think you can ever beat array() performance in PHP code; and in most cases ArrayAccess implementations just wrap an array. You can foreach() ordinary objects, too.

  21. Tarjei Huse says:
    July 20th, 2010 at 7:21 pm

    A solution would be to let the array type implement the Traversable and Countable interfaces, i.e.:

    $it = array(1,2,3);
    if ($it instanceof Traversable) {
    foreach($it as $val) print $val;
    }
    if ($it instanceof Countable) {
    print “There are ” . count($it) . ” items”;

    }

  22. Jack Stevenson says:
    July 20th, 2010 at 10:53 pm

    No, is_array() should only match arrays. A new function called like_array() would be a good idea.

  23. Richard Lynch says:
    July 21st, 2010 at 5:41 am

    No.
    It’s not an array.
    It’s some goofy SPL thing that I can never remember the names of.
    You are in a maze of twisty little passages, all alike.
    If you need to write some code that accepts an array or one of these goofy things, then write a function to test for valid mixed input.

  24. Helpdesk says:
    July 21st, 2010 at 10:42 am

    Itarators are not arrays!

    if($array instanceof Traverable){
    $array = iterator_to_array($array);
    }

    works on recursive iterators as well.

  25. Bazmo says:
    July 22nd, 2010 at 3:49 am

    This issue is a real thorn in my side. What would be really nice is a is_iteratable type function just for checking before the foreach

  26. Nieruchomości Jelenia Góra says:
    July 24th, 2010 at 11:11 am

    Thx I gotta using it now :)

  27. Douglas Greenshields says:
    October 18th, 2010 at 8:20 pm

    The clue is, as ever, in the question. If you’re writing a test to check whether something is an array, and you’re writing object-oriented code, the question has to be “why?”. Are you actually checking the type, because you are specifically expecting that type, or are you checking whether the variable can be used like an array (generally, it would surely be the latter)? So the answer isn’t to re-define is_array() – that’s fine for checking for type, should you need to do that. Maybe it requires another built-in function, or functions (like is_iterable(), although of course plain old objects are iterable). Or maybe the answer is by allowing arrays to be referred to by object syntax like instanceof, rather than making objects more like “arrays”.