skip to content

In PHP 8, why should in_array() and array_search() usually be called with the strict flag, and how do you test array_search()'s result?

level: middleimportance: should knowfreq 48%

answer

  1. third parameter $strict defaults to false
  2. loose == juggling on every element
  3. null matches 0, '' and false
  4. array_search can return key 0
  5. compare the result with === false

basics

~20 s

Without strict true, in_array() and array_search() compare with ==, so null matches 0 and '', and '1e1' matches '10'. array_search() returns the key or false, and key 0 is falsy, so test its result with === false.

solid answer

~40 s

`in_array(mixed $needle, array $haystack, bool $strict = false)` and `array_search()` compare the needle to each element with **loose** `==` unless the third argument is `true`. Loose comparison still juggles types in PHP 8: `null` equals `0`, `''`, `false` and `[]`; two numeric strings compare as numbers, so `'1e1'` matches `'10'`; a non-empty string equals `true`. PHP 8.0 did remove the worst case, a non-numeric string matching `0`, which is why `in_array('abc', [0])` is now `false`. With `$strict = true` both type and value must match, which is almost always the intent for IDs, codes and status values. `array_search()` returns the **key** of the first match or `false`, and since the key can be `0` or `''`, both falsy, its result must be checked with `=== false`, never with `!`.

code

php · 15 lines
php
<?php
$statuses = ['paid', 'packed', 'out'];
var_dump(in_array(true, $statuses));        // bool(true): 'paid' == true
var_dump(in_array(true, $statuses, true));  // bool(false)

$menu = ['croissant', 'baguette'];
$key = array_search('croissant', $menu, true);
if ($key === false) {
    echo "not on the menu\n";
} else {
    echo "found at key $key\n";              // found at key 0
}

var_dump(in_array(null, [0]));              // bool(true)
var_dump(in_array(0, ['abc']));             // bool(false) since PHP 8.0

go deeper

for a junior

Recall that in_array() and array_search() are loose unless the third argument is true, and that array_search() returns a key or false.

for a middle

Explain which loose matches survive PHP 8 (null, numeric strings, true) and why array_search() needs an === false check because keys can be 0.

for a senior

Enforce strict searches for IDs, statuses and allow-lists in review, normalise mixed int and string data, and replace repeated scans with keyed lookups.

for a principal

Decide how the codebase handles type consistency at data boundaries so searches can be strict everywhere without surprising mismatches.

## Two search helpers, one flag PHP's value-search helpers share a signature: - `in_array(mixed $needle, array $haystack, bool $strict = false): bool` answers *is this value present?* - `array_search(mixed $needle, array $haystack, bool $strict = false): int|string|false` answers *under which key is it?* and returns the first matching key, or `false`. `array_keys($array, $filter_value, $strict)` also has the same flag when you want every matching key. In all three, `$strict = false` means each element is compared with `==`, and `$strict = true` means `===`. ## What loose comparison still matches in PHP 8 PHP 8.0 changed how a number compares with a non-numeric string, so `0 == 'abc'` is now `false`. Many other conversions remain, and each one becomes a false positive inside a loose search: | Call | Result | Why | |---|---|---| | `in_array(null, [0])` | `true` | `null` and `0` both convert to `false` | | `in_array(null, [''])` | `true` | `null` compares equal to the empty string | | `in_array('1e1', ['10'])` | `true` | both are numeric strings, compared as numbers | | `in_array('abc', [true])` | `true` | a non-empty string converts to `true` | | `in_array(0, ['abc'])` | `false` | since 8.0; it was `true` in PHP 7 | | `in_array('7', [7])` | `true` | numeric string equals the int | The last row is often the reason code uses loose search on purpose: IDs read from a form arrive as strings while IDs from a database driver may be integers. That is better solved by normalising types first than by relying on juggling. ## Strict mode With `true` as the third argument, an element matches only if it has the same type and value as the needle: - `in_array('7', [7], true)` is `false`. - `in_array(null, [0, '', false], true)` is `false`. - Objects match only when they are the same instance. In a bakery-delivery app, checking an order status against `['paid', 'packed', 'out']` or an allow-list of delivery slot codes should use strict mode, so that a `null` or `true` coming from an unexpected code path cannot slip through. ## The array_search() return trap `array_search()` returns a key, and keys can be `0` or `''`. Both are falsy, so this common pattern is wrong: ```php if (!array_search('croissant', $menu)) { // runs when croissant is NOT found, and ALSO when it is at key 0 } ``` The correct test compares identically with `false`: 1. `$key = array_search('croissant', $menu, true);` 2. `if ($key === false) { /* not found */ }` 3. Otherwise `$key` is a valid key, even if it is `0`. The same care applies when passing the result on: `$menu[$key]` with `$key === false` reads key `0` because `false` is converted to `0`. ## Performance note Both functions scan the array from the start, comparing element by element, so a search is linear in the array's size. When the same haystack is searched many times, for example checking thousands of order lines against a list of discontinued products, build a lookup array keyed by the value once (`array_flip()` does this for string and int values) and test keys with `array_key_exists()`. That trades one pass of preparation for direct key lookups afterwards; note that key lookup is always exact by the converted key, not loose. ## Takeaways - Pass `true` as the third argument unless you deliberately want type juggling. - Compare `array_search()` results with `=== false`. - Normalise mixed int/string data before searching rather than relying on `==`. - For repeated membership tests, index the values as keys.

  • How would you speed up checking thousands of order lines against a list of discontinued product codes?
    Build a lookup once with `array_flip($discontinued)`, so the codes become keys, then test each line with `array_key_exists($code, $lookup)`. That replaces a linear `in_array()` scan per line with a key lookup. Watch key casting: numeric-string codes become integer keys, which is fine for lookups but not when reading keys back.
  • What happens if code uses array_search()'s result as a key without checking for false?
    `false` used as an array key is converted to `0`, so `$menu[$key]` silently reads the element at key 0 when nothing was found. Always test `$key === false` first.

saying these in an interview costs you the question

  • in_array() compares strictly by default.
  • Since PHP 8, loose comparison is safe, so strict mode is unnecessary.
  • if (!array_search(...)) correctly detects a missing value.
  • in_array(null, [0]) is false because null is not a number.
  • array_search() returns the value it found, or null if absent.