php: write php 8.4’s array_find from scratch
Tags: php
Published: 2024-07-09 13:15:03

there’s an rfc vote currently underway for a number of [new array functions in php8.4](https://laravel-news.com/php-8-4-array-find-functions) (thanks to [symfony station](https://symfonystation.mobileatom.net/) for pointing this out!). the proposal is for four new functions for finding and evaluating arrays using `callables`. the functions are:

* array_find()
* array_find_key()
* array_any()
* array_all()

the full details can be read [here](https://wiki.php.net/rfc/array_find).

if we’re impatient, though, we can skip waiting for php8.4 and homeroll these ourselves.

## array_find()
at it’s heart, the functionality of `array_find` is [`array_filter`](https://www.php.net/manual/en/function.array-filter.php). the major difference is that `array_find` returns only the *first* element of the array, not all of them.

with that in mind, we can build our own version like so:

```php
/**
 * Find the first value in an array that evaluates to true in $func
 *
 * @param  array $array
 * @param  callable $func
 * @return any
 */
$array_find = function(array $array, callable $func) {
    return array_values(array_filter($array, $func))[0] ?? null;
};
```

when we run this against the test data given in the rfc, we get the expected results.

```php
$array = [
    'a' => 'dog',
    'b' => 'cat',
    'c' => 'cow',
    'd' => 'duck',
    'e' => 'goose',
    'f' => 'elephant'
];
$array_find($array, function (string $value) {
    return strlen($value) > 4;
}); // string(5) "goose"
```

## array_find_key()
the only difference between `array_find_key` and `array_find` is that the one that is called ‘find_key’ finds the key, not the value. no surprise.

we can implement this by taking our `array_find code` and adding [`array_key_first`](https://www.php.net/manual/en/function.array-key-first.php):

```php
/**
 * Find the first key in an array where the value evaluates to true in $func
 *
 * @param  array $array
 * @param  callable $func
 * @return any
 */
$array_find_key = function(array $array, callable $func) {
    return array_key_first(array_filter($array, $func)) ?? null;
};
```

##array_any() and array_all()
both `array_any` and `array_all` return booleans.

if one or more of the elements in the array passes the test in the callable, `array_any will` return true. we can achieve this by filtering the array and testing if the `count` of the result is greater than zero:

```php
/**
 * Evaluate if any element in array $array evaluates to true in $func
 *
 * @param  array $array
 * @param  callable $func
 * @return bool
 */
$array_any = function(array $array, callable $func): bool {
    return (bool)count(array_filter($array, $func));
};
```

for `array_all`, all the elements in the array must pass the callable test. this requires us to test if the number of elements in the filtered array is the same as in the original array before filtering, ie. if all the elements passed the filter.

```php
/**
 * Evaluate if all elements in array $array evaluates to true in $func
 *
 * @param  array $array
 * @param  callable $func
 * @return bool
 */
$array_all = function(array $array, callable $func): bool {
    return count(array_filter($array, $func)) == count($array);
};
```

and now we have a small slice of the power of php8.4 without the wait.

> 🔎  this post was originally written in the [grant horwood technical blog](https://gbh.fruitbat.io/2024/05/21/php-write-php-8-4s-array_find-from-scratch/)