Home » Php » php – Why does in_array() return unexpected / strange results?

php – Why does in_array() return unexpected / strange results?

Posted by: admin July 12, 2020 Leave a comment

Questions:

Why is in_array() sometimes behaving so strangely and returns such unexpected results?

Let’s have a look at a few examples:

$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)

Huh? What’s happening here!?

(Some years ago I wondered about this, at first, strange behaviour. I thought it might be useful for some people though, thus I entered this question.)

How to&Answers:

Solution (in short):

Use in_array() always with the third parameter 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)

So when you use in_array() with true as third parameter, the comparison between the searched value and the array is done strictly, meaning in_array() works like you would probably expect it.

(The parameter strict is also described in the php.net documentation.)

Explanation

Without the parameter strict set to true, the comparison between the searched value and each value of the array is done by equality and not by identity. This means that the value’s type does not matter and thus PHP converts the values internally to the same data type in order to be able to compare them.

This means that in the first example, the searched value 'Gary' is converted to a boolean when it’s compared with true, so it results in the comparison of true with true, which is obviously true.

The same goes with the second array, where 0 is finally compared with null, resulting in true, even though 0 is obviously not the same as null (this can be especially tricky when you work with numbers and / or function results for example, where null can express an empty value and not 0).

The third array then looks really weird, because we check for the value true in the array, which only contains -1, but in_array() still returns true for the comparison. In this case the -1 gets converted to a boolean true. So the problem is the same in both directions.

You can find more examples about the comparison problem in PHP (because this is the same as == / ===) in this Stack Overflow answer.

Unfortunately, the default for the strict parameter when calling in_array() is… well, yeah, false. :-/ PHP and it’s typing…

Consequences

You should really never ever call in_array() without the strict parameter set to true. When you do not have arrays of mixed types and you only check the values with the same type, in_array() works as expected. See this example:

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

So at least this works as expected. But in my opinion, it is much easier to just always call in_array() with strict true. (Similiar to the “SQL Injection Problem”… Just always use PDO and prepared statements so you’re safe, even though it’s a query without variable parameters. You’re always safe then.)

Be careful though

You should definitely call in_array() with strict true. But there is one downside though, I do want to mention (even though it is obvious). You must definitely use the correct types when calling in_array() then:

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

But you can just use Type Casting, when you know that you compare numbers:

$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)

Well, yeah… Just use it with strict set to true. 😉