如何在PHP中检查数组?

17

我能想到的最好的是

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

呃,有没有更好看的东西?

编辑:对于is_object的测试似乎是不必要的。我已经在PHP手册的instanceof部分添加了相关说明。


2
你的方法对我来说似乎还不错。有更好的方法吗?我怀疑这点。 - Nemoden
2
你是想确定这个变量能否像数组一样被访问(例如 $array['test'])还是你可以像遍历数组一样迭代它? - JamesArmes
1
你能为这个函数提供一个使用案例吗?毕竟你想知道它是否符合其中任何一个条件。不像数组。 - paulmatthews86
4个回答

12

既然你在帖子中使用了“pretty”这个词,那么我只是快速建议一下如何进行排版美化以提高可读性:

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

修改说明:

  1. 函数名称更改:“is_array_alike” -> “is_array_or_alike”,以便清楚地测试数组和类似性。

  2. 参数/参数名称更改:$array -> $var,因为“$array”已经预设该参数的类型为数组。

  3. 使用与Drupal编码标准相一致的可读性更好的&&条件堆叠:每行最多80字符。由于您是Drupal核心维护者之一,我认为您在提交之前可能已经做过这个工作。

  4. 您正确地指出了is_object()是不必要的。在Java中,它是必需的,因为如果第一个操作数不是对象,则instanceof会引发编译时错误,但我刚才在PHP中检查了,没有错误,只有bool(false)的结果。

我同意paulmatthews86的建议,即提供用例。如果我们不知道标准,则很难提供建议。例如,借鉴鸭子类型范例的观点,instanceof测试是有用但不一定强制或完整的。如果您更关心$var可以做什么以及在某些情况下它如何行为,则可以使用反射来检查稍后需要调用的方法是否存在,或者您可以测试当传递给数组函数时它的行为是否符合预期。例如,是否与array_udiff_assocarray_chunk等“工作”。如果这些行为对您的用例更重要,则这些测试可能会取代instanceof类型测试,尽管当然会有很多重叠。

希望这可以帮助您。如果您决定发布,请告诉我们您最终决定了什么。


是的,这是Drupal核心中Twig和渲染系统之间(暂时的?)BC层的一部分。为了使渲染工作正常,我们需要将渲染数组变成ArrayAccess,以便更改正确传播。另一方面,系统中有许多is_array检查现在失败了。 - chx
很好。希望到目前为止,转换到Twig的过程顺利进行。我快速搜索了最新的Twig源代码,并看到了对ArrayAccess、Traversable和Countable的instanceof检查,但没有找到任何“instanceof Serializable”的测试。你是否包含Serializable是有特定原因的? - David

1

这与所问的问题完全无关。 - chx
1
数组性质与可迭代性息息相关,不是吗? - Notepad

1

即使 ArrayObject 实现了 Serializable,但错误的假设是一个对象必须实现它才能像数组一样。

相反的情况是这样的。您可以利用 Serializable 有效地防止对象的序列化。因此,我建议从接口检查中删除它。

此外,如果您在没有对象的情况下使用 instanceof,您将看到一个(致命的)错误。我建议您将“类似数组”的对象的检查委托给叶子函数,并让输入类型较不严格的函数使用它:

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
        ;
}

请注意,即使一个对象类似于数组,它也不能与大多数数组函数一起使用。也不适用于foreach,因此您最好明确说明您在这里寻找哪些数组功能。

测试一下,instanceof 在非对象上使用时不会致命错误。在这一点上,你的说法是错误的。我完全不知道你在这里关于“Serializable”的意思。如果你的意思是__sleep()__wakeup不被支持,那么Serializable允许一个unserialize方法来获取序列化数据,因此它可以做更多的事情而不仅仅是__wakeup - chx

0

PHP 7.3有一个新规范,实现了一个is_countable函数,链接在这里https://wiki.php.net/rfc/is-countable

根据此规范和先前的PHP版本,您可以使用此函数。

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

或者你也可以像这样实现一种类似 polyfill 的东西

if (!function_exists('is_countable')) {

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

}

这个 $array instanceof ArrayAccess && $array instanceof Traversable && $array instanceof Serializable 怎么样? - chx
@chx 我不完全确定为什么PHP RFC只指定了单个实例检查,但我想他们肯定有他们的理由,虽然我也想知道。 - Brian Leishman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接