Как проверить массивность в PHP?

Лучшее, что я мог придумать, это

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 кажется ненужным. Я добавил раздел об этом в руководство instanceof PHP.


person chx    schedule 10.09.2012    source источник
comment
Ваш подход кажется мне приемлемым. Is there something more pretty? сомневаюсь.   -  person Nemoden    schedule 10.09.2012
comment
Вы пытаетесь определить, можно ли получить доступ к переменной как к массиву (например, $array['test']) или вы можете перебирать ее как массив?   -  person JamesArmes    schedule 10.09.2012
comment
Можете ли вы привести пример использования этой функции? Конечно, вы хотели бы знать, было ли это одним из этих условий. Не похоже на массив.   -  person paulmatthews86    schedule 10.09.2012


Ответы (4)


Что ж, поскольку вы использовали слово «симпатичный» в своем посте, просто небольшое предложение по косметическим изменениям для удобства чтения:

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, я предполагаю, что эта функция может войти в Drupal, так что, вероятно, вы все равно сделали бы это перед фиксацией.

  4. Вы правы, что is_object() не нужен. В Java это было бы необходимо, потому что instanceof выдавало бы ошибку времени компиляции, если бы первый операнд не был объектом, но я только что проверил в PHP, и ошибки нет, просто результат bool(false).

Я поддерживаю предложение paulmatthews86 о том, чтобы вы предоставили вариант использования. Трудно дать рекомендации, если мы не знаем критериев. Например, чтобы немного позаимствовать с точки зрения парадигмы утиного набора, тесты instanceof были бы полезны, но не обязательно обязательными или даже полными. Если вас больше интересует, что может делать $var и как он ведет себя в определенных контекстах, вы можете использовать отражение, чтобы проверить наличие методов, которые необходимо вызвать для него позже, или вы можете проверить, ведет ли он себя так, как ожидалось, когда передается функциям массива. например «Работает» ли он с array_udiff_assoc, array_chunk и т. д. Если эти варианты поведения более важны для ваших вариантов использования, то эти тесты могут заменить тестирование типов instanceof, хотя, конечно, будет много дублирования.

Надеюсь это поможет. Интересно посмотреть, что вы, наконец, решите, если решите опубликовать это.

person David    schedule 11.09.2012
comment
Да, это должно быть частью (временного?) уровня BC в ядре Drupal между Twig и системой рендеринга. Чтобы рендеринг работал, нам нужно, чтобы массивы рендеринга стали ArrayAccess, чтобы изменения распространялись правильно. OTOH, в системе есть много проверок is_array, которые теперь терпят неудачу. - person chx; 12.09.2012
comment
Прохладный. Надеюсь, переход на Twig пока идет хорошо. Я сделал быстрый поиск последнего исходного кода Twig и увидел проверки instanceof для ArrayAccess, Traversable и Countable, но не нашел тестов instanceof Serializable. Вы включаете Serializable по определенной причине? - person David; 12.09.2012

в php есть интерфейс итератора http://php.net/manual/en/class.iterator.php

использовать

function isIter($abc) {
    return (is_array($abc) || $abc instanceof Traversable);
}
person Notepad    schedule 10.09.2012
comment
Это вообще не имеет отношения к заданному вопросу. - person chx; 10.09.2012
comment
arrayness поставляется с iterable.is'nt it. - person Notepad; 10.09.2012

Несмотря на то, что 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, поэтому вам лучше указать, какие функции массива вы ищете здесь.

person hakre    schedule 04.10.2012
comment
Проверьте это, instanceof не является фатальным при использовании на не-объекте. Вы на самом деле ошибаетесь в этом. Я совершенно не понимаю, что вы говорите о Serializable здесь. Если вы имеете в виду, что __sleep() и __wakeup не поддерживаются, Serializable позволяет использовать метод unserialize, который получает сериализованные данные, поэтому он может делать больше, чем просто __wakeup. - person chx; 05.10.2012

Есть новая спецификация для PHP 7.3, которая реализует функцию is_countable здесь https://wiki.php.net/rfc/is-countable

Основываясь на спецификации и для предыдущих версий PHP, вы можете использовать это

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

или вы также можете реализовать своего рода полифилл для этого

if (!function_exists('is_countable')) {

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

}
person Brian Leishman    schedule 18.04.2018
comment
А как насчет этих $array instanceof ArrayAccess && $array instanceof Traversable && $array instanceof Serializable. - person chx; 18.04.2018
comment
@chx Я не совсем уверен, почему PHP RFC указывает только проверку одного экземпляра, но я предполагаю, что у них есть свои причины, хотя я также хотел бы знать - person Brian Leishman; 18.04.2018