In PHP 8.4 and 8.5, what do array_find(), array_any(), array_all(), array_first() and array_last() return, and what did they replace?
answer
- 8.4: array_find, array_find_key, array_any, array_all
- callback gets value then key
- array_find returns null when nothing matches
- array_all([]) is true, array_any([]) false
- 8.5: array_first/array_last, null if empty
basics
~20 sPHP 8.4 added array_find() (first matching value or null), array_find_key(), array_any() and array_all(), which stop at the deciding element. PHP 8.5 added array_first() and array_last(), returning the first or last value or null for an empty array.
solid answer
~40 sPHP 8.4 added four search helpers whose callbacks receive `($value, $key)`. `array_find()` returns the **first value** for which the callback is truthy, or `null`; `array_find_key()` returns that element's **key**, or `null`. `array_any()` returns `true` as soon as one element passes, and `array_all()` returns `false` as soon as one fails; on an empty array `array_any()` is `false` and `array_all()` is `true`. All four stop at the deciding element, unlike the older idioms `array_filter()` plus `reset()` or `count()`, which test everything and build an array. PHP 8.5 added `array_first()` and `array_last()`, which return the first or last value, or `null` for an empty array, without the by-reference internal pointer handling of `reset()`/`end()` or the two-step `$a[array_key_first($a)]`. Because `null` doubles as "not found", use the key-returning variants when values may be `null`.
code
php · 15 lines<?php
$orders = [
'A1' => ['customer_id' => 42, 'status' => 'paid'],
'A2' => ['customer_id' => 17, 'status' => 'unpaid'],
'A3' => ['customer_id' => 17, 'status' => 'paid'],
];
$first17 = array_find($orders, fn (array $o) => $o['customer_id'] === 17); // A2's row
$key17 = array_find_key($orders, fn (array $o) => $o['customer_id'] === 17); // 'A2'
var_dump(array_any($orders, fn (array $o) => $o['status'] === 'unpaid')); // bool(true)
var_dump(array_all($orders, fn (array $o, string $k) => $k[0] === 'A')); // bool(true)
var_dump(array_all([], fn () => false)); // bool(true)
var_dump(array_first($orders)['status']); // string(4) "paid"
var_dump(array_last([])); // NULLgo deeper
Recall that PHP 8.4 added array_find(), array_any() and array_all(), and PHP 8.5 added array_first() and array_last(), all returning null or a bool as documented.
Explain the value-then-key callback, early stopping, the empty-array results of array_any() and array_all(), and the null ambiguity that array_find_key() resolves.
Replace filter-then-reset idioms in review, guard validation that relies on array_all() against empty input, and respect the minimum PHP version the code must support.
Decide when to adopt new standard-library helpers across a codebase, weighing readability against supported versions, polyfills and team familiarity.
## Why new functions were needed Before PHP 8.4, finding the first element that matched a condition meant either a hand-written `foreach` with `break`, or a combination such as `array_filter()` followed by `reset()` or `array_key_first()`. The combination has two drawbacks: it tests **every** element even when the first one matches, and it allocates a filtered array only to throw most of it away. Checking "is any order unpaid?" had the same problem with `count(array_filter(...)) > 0`. ## The PHP 8.4 search helpers All four take the array and a callback, and all four call the callback as `$callback($value, $key)`: | Function | Returns | Stops when | Empty array | |---|---|---|---| | `array_find($a, $cb)` | first value whose callback is truthy, else `null` | first match | `null` | | `array_find_key($a, $cb)` | key of that element, else `null` | first match | `null` | | `array_any($a, $cb)` | `true` if any element passes | first pass | `false` | | `array_all($a, $cb)` | `true` if every element passes | first failure | `true` | The empty-array results follow logic, not convenience: "all of no elements pass" is vacuously true, and "some element passes" cannot be true when there are none. That matters for validation code: `array_all($lines, $isPaid)` on an order with no lines returns `true`, so check for emptiness separately if an empty order is invalid. In a bakery-delivery app: - `array_find($orders, fn ($o) => $o['customer_id'] === 17)` returns the first order of customer 17. - `array_any($orders, fn ($o) => $o['status'] === 'unpaid')` answers whether the driver may leave. - `array_all($slots, fn ($s, $k) => $s !== '' && is_int($k))` validates a slot list using both value and key. ## The null ambiguity `array_find()` returns `null` both when nothing matches and when the matching element's value is `null`. When `null` is a legitimate value, call `array_find_key()` instead and compare the result with `null`; a found key is always an `int` or `string`, so `null` unambiguously means "not found". ## The PHP 8.5 accessors `array_first(array $array): mixed` and `array_last(array $array): mixed` return the first and last **value** in iteration order, or `null` for an empty array. They replace three older idioms, each with a catch: 1. `reset($a)` and `end($a)` take the array **by reference** and move its internal pointer, so they cannot be called on a function's return value without a notice, and they return `false` for an empty array, which is indistinguishable from a stored `false`. 2. `$a[array_key_first($a)]` (available since 7.3) needs two steps, and on an empty array `array_key_first()` returns `null`, so the read raises the 8.5 null-offset deprecation and an undefined-key warning. 3. `array_values($a)[0]` copies the whole array to read one element. `array_first()` and `array_last()` take the array by value, do not touch the internal pointer, and work on any expression. The same `null` caveat applies: when an array may hold `null`, check emptiness first or use `array_key_first()`/`array_key_last()`. ## Version awareness These helpers are recent, so a codebase that must still run on PHP 8.3 cannot call them. Such code needs its own fallback, usually a short `foreach`, until the minimum version moves. On the current release (8.5) they are the idiomatic choice, and they read closer to the intent than the older combinations.
- How do you tell 'no match' from 'the match is null' with array_find()?You cannot from `array_find()` alone, since it returns `null` for both. Use `array_find_key()`: a found key is always an `int` or `string`, so a `null` result reliably means no element matched, and you can then read the value by that key.
- Why is array_first($a) preferable to reset($a) for reading the first value?`reset()` takes the array by reference and moves its internal pointer, so it needs a variable and has a side effect, and it returns `false` for an empty array. `array_first()`, added in PHP 8.5, takes the array by value, leaves the pointer alone, accepts any expression and returns `null` when empty.
saying these in an interview costs you the question
- array_find() returns the key of the first matching element.
- array_all() returns false for an empty array.
- array_any() tests every element before returning.
- array_find() callbacks receive only the value, never the key.
- array_first() returns false for an empty array, like reset().