Почему in_array() возвращает неожиданные/странные результаты?

Почему in_array() иногда ведет себя так странно и возвращает такие неожиданные результаты?

Давайте рассмотрим несколько примеров:

$arrayWithTrue = ['Andreas', 'Philipp', true];
$arrayWithNull = [1, 2, 3, null];
$arrayWithMinusOne = [-1];

var_dump(in_array('Gary', $arrayWithTrue)); // returns bool(true)
var_dump(in_array(0, $arrayWithNull)); // returns bool(true)
var_dump(in_array(true, $arrayWithMinusOne)); // returns bool(true)

А? Что здесь происходит!?

(Несколько лет назад я задавался этим вопросом, поначалу, странным поведением. Я думал, что это может быть полезно для некоторых людей, поэтому я ввел этот вопрос.)

Ответ 1

Решение (короче):

Используйте in_array() всегда с третьим параметром strict true:

$arrayWithTrue = ['Andreas', 'Philipp', true];
$arrayWithNull = [1, 2, 3, null];
$arrayWithMinusOne = [-1];

var_dump(in_array('Gary', $arrayWithTrue, true)); // returns bool(false)
var_dump(in_array(0, $arrayWithNull, true)); // returns bool(false)
var_dump(in_array(true, [-1], true)); // returns bool(false)

Поэтому, когда вы используете in_array() с true в качестве третьего параметра, сравнение между искомым значением и массивом выполняется строго, значение in_array() работает так, как вы ожидали бы этого.

(Параметр strict также описан в документации php.net.)

Описание

Без параметра strict, установленного в true, сравнение найденного значения и каждого значения массива выполняется с помощью равенства, а не идентификатора. Это означает, что тип значения не имеет значения, и, таким образом, PHP преобразует значения внутри одного и того же типа данных, чтобы иметь возможность их сравнивать.

Это означает, что в первом примере искомое значение 'Gary' преобразуется в булево, когда оно сравнивается с true, поэтому оно приводит к сравнению true с true, что, очевидно, true.

То же самое происходит со вторым массивом, где 0, наконец, сравнивается с null, что приводит к true, хотя 0, очевидно, не совпадает с null (это может быть особенно сложно, если вы работаете с числами и/или результатами функций, например, где null может выразить пустое значение, а не 0).

Третий массив выглядит действительно странно, потому что мы проверяем значение true в массиве, которое содержит только -1, но in_array() все еще возвращает true для сравнения. В этом случае -1 преобразуется в boolean true. Таким образом, проблема в обоих направлениях одинакова.

Вы можете найти больше примеров проблемы сравнения в PHP (потому что это то же самое, что ==/===) в этом ответе.

К сожалению, по умолчанию для строкового параметра при вызове in_array() есть... ну, да, false.: -/PHP и он печатает...

Последствия

Вы никогда не должны называть in_array() без строгой настройки параметра true. Если у вас нет массивов смешанных типов, и вы проверяете только значения одного типа, in_array() работает так, как ожидалось. См. Этот пример:

$arrayWithStrings = ['Andreas', 'Philipp', 'Friedrich'];
var_dump(in_array('Gary', $arrayWithStrings)); // returns bool(false)

Итак, по крайней мере, это работает так, как ожидалось. Но, на мой взгляд, гораздо проще просто называть in_array() строгим true. (Совместим с "проблемой впрыска SQL"... Всегда используйте PDO и подготовленные операторы, чтобы вы были в безопасности, даже если это запрос без переменных параметров. В любом случае вы всегда в безопасности.)

Будьте осторожны, хотя

Вы должны обязательно называть in_array() строгим true. Но есть один недостаток, хотя я хочу упомянуть (хотя это очевидно). Вы должны обязательно использовать правильные типы при вызове in_array(), а затем:

$arrayWithNumbers = [1, 2, 3];
var_dump(in_array('1', $arrayWithNumbers, true)); // returns bool(false)

Но вы можете просто использовать Type Casting, когда знаете, что сравниваете числа:

$arrayWithNumbers = [1, 2, 3];
var_dump(in_array((int)'1', $arrayWithNumbers, true)); // returns bool(true)

Bonus

// Comparing false with an empty array
var_dump(in_array(false, [[]])); // returns bool(true)

Ну, да... Просто используйте его со строгим набором true.; -)