17

The best I could come up is

function is_array_alike($array) {
  return is_array($array) || (is_object($array) && $array instanceof ArrayAccess && $array instanceof Traversable && $array instanceof Serializable && $array instanceof Countable);
}

Ugh. Is there something more pretty?

Edit: the test for is_object seems unnecessary. I have added a section to the instanceof PHP manual about that.

chx
  • 11,270
  • 7
  • 55
  • 129
  • 2
    Your approach seems to be okay to me. `Is there something more pretty?` doubt it. – Nemoden Sep 10 '12 at 06:44
  • 2
    Are you trying to determine if the variable can be accessed like an array (e.g. `$array['test']`) or that you can iterate over it like an array? – JamesArmes Sep 10 '12 at 12:58
  • 1
    Can you give a use case for this function? Surely you'd want to know if it was any one of those conditions. Not like an array. – paulmatthews86 Sep 10 '12 at 17:21

4 Answers4

12

Well, since you did use the word "pretty" in your post, just a quick suggestion on cosmetic changes for readability:

function is_array_or_alike($var) {
  return is_array($var) ||
           ($var instanceof ArrayAccess  &&
            $var instanceof Traversable  &&
            $var instanceof Serializable &&
            $var instanceof Countable);
}

Explanation of changes:

  1. Function name change: "is_array_alike" -> "is_array_or_alike" just to make it clear that both array-ness and alike-ness are being tested.

  2. Param/arg name change: $array -> $var because "$array" is already sort of presupposing that the argument is of type array.

  3. Stacking &&'ed conditions for readability and conformance to Drupal coding-standard: 80-char line max. Since you are one of the Drupal core maintainers I guess I am assuming this function might possibly go into Drupal, so probably you would have done this before committing anyway.

  4. You are correct that the is_object() is unnecessary. In Java it would be necessary because instanceof would throw a compile-time error if the first operand weren't an object, but I checked just now in PHP and there is no error, just a result of bool(false).

I second paulmatthews86's suggestion that you provide a use case. It's hard to provide recommendations if we don't know the criteria. For example, to borrow a bit from the duck-typing paradigm point of view, the instanceof tests would be useful but not necessarily mandatory or even complete. If you are more interested in what the $var can do and how it behaves in certain contexts then you could use reflection to check for the existence of the methods that need to be invoked on it later, or you could test that it behaves as expected when passed to array functions. e.g. Does it "work" with array_udiff_assoc, array_chunk, etc. If these behaviors are more important for your use cases then these tests might supersede the instanceof type-testing, although of course there would be a lot of overlap.

Hope this helps. Interested to see what you finally decide upon if you decide to post it.

David
  • 751
  • 3
  • 13
  • Yes, this is intended to be part of a (temporary?) BC layer in Drupal core between Twig and the rendering system. To make the rendering work, we need render arrays to become ArrayAccess so that changes propagate properly. OTOH there are many is_array checks in the system which now fail. – chx Sep 11 '12 at 21:07
  • Cool. Hope the transition to Twig is going well so far. I did a quick grep of latest Twig source and saw instanceof checks for ArrayAccess, Traversable, and Countable, but didn't find any "instanceof Serializable" tests. Are you including Serializable for a specific reason? – David Sep 11 '12 at 22:53
1

there is iterator interface in php http://php.net/manual/en/class.iterator.php

use

function isIter($abc) {
    return (is_array($abc) || $abc instanceof Traversable);
}
Notepad
  • 1,659
  • 1
  • 12
  • 14
1

Even though ArrayObject implements Serializable, the assumption is wrong that an object must implement it to be array-like.

The opposite is the case. You can make use of Serializable to effectively prevent the serialization of an object. So I would consider to remove it from the interface check.

Also if you use instanceof without an object, you will see a (fatal) error. I suggest you delegate the check of an object that is "array-like" to a leaf function and make your function that is less strict with the input types make use of it:

function is_array_or_object_arraylike($var) {

    return is_array($var) 
        || (is_object($var) && is_object_arraylike($var))
        ;
}

function is_object_arraylike($obj) {

    return $obj instanceof ArrayAccess
        && $obj instanceof Traversable
        && $obj instanceof Countable
        ;
}

Also keep in mind, even an object is array-like, it does not work with most array functions. nor would it work well with foreach, so you should better outline about which array features you're looking for here.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • Test it, instanceof doesn't fatal when used on a non-object. You are factually wrong on that one. I absolutely have no idea of what are you saying about `Serializable` here. If you mean `__sleep()` and `__wakeup` is not supported, `Serializable` allows for an `unserialize` method which gets the serialized data so it can do more than just `__wakeup`. – chx Oct 04 '12 at 23:17
0

There's a new spec out for PHP 7.3 that implements an is_countable function here https://wiki.php.net/rfc/is-countable

Based on the spec & for previous PHP versions, you can use this

if (is_array($foo) || $foo instanceof Countable) {
    return count($foo);
}

or you could also implement a sort of polyfill for that like this

if (!function_exists('is_countable')) {

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

}
Brian Leishman
  • 8,155
  • 11
  • 57
  • 93
  • What about these `$array instanceof ArrayAccess && $array instanceof Traversable && $array instanceof Serializable`. – chx Apr 18 '18 at 17:21
  • @chx I'm not totally sure why the PHP RFC only specifies the single instance check, but I would assume they have their reasons, although I would also like to know – Brian Leishman Apr 18 '18 at 17:25