rfc:array_find

PHP RFC: array_find

Introduction

This RFC proposes the addition of new array functions: array_find, array_find_key, array_any and array_all, which are helper functions for common patterns of checking an array for the existence of elements matching a specific condition.

There are currently several functions processing arrays using a callback. However still missing are functions to find a single element matching a condition and the closely related functions of checking whether or not there are elements matching a condition. Implementing these functions in userland is relatively simple, but these functions are often required, leading to the wheel being reinvented over and over. Furthermore these type of functions are implemented in other programming languages like Rust, JavaScript or C++, too. Therefore there is a reason to include these functions as standard with the next PHP version. In addition, the implementation of these functions is quite similar to array_filter and relatively trivial to implement, so the maintenance effort should be low.

Proposal

This RFC proposes to add four new function, array_find, array_find_key, array_any and array_all.

array_find

array_find returns the value of the first element for which the $callback returns true. If no matching element is found the function returns NULL.

function array_find(array $array, callable $callback): mixed {
    foreach ($array as $key => $value) {
        if ($callback($value, $key)) {
            return $value;
        }
    }
 
    return null;
}

Parameters

array $array

The array that should be searched.

callable $callback

The callback function to call to check each element. The first parameter contains the value, the second parameter contains the corresponding key. If this function returns true, the value is returned from array_find and the callback will not be called for further elements.

Return Value

The function returns the value of the first element for which the $callback returns true. If no matching element is found the function returns NULL.

Examples

$array = [
    'a' => 'dog',
    'b' => 'cat',
    'c' => 'cow',
    'd' => 'duck',
    'e' => 'goose',
    'f' => 'elephant'
];
 
// Find the first animal with a name longer than 4 characters.
var_dump(array_find($array, function (string $value) {
    return strlen($value) > 4;
})); // string(5) "goose"
 
// Find the first animal whose name begins with f.
var_dump(array_find($array, function (string $value) {
    return str_starts_with($value, 'f');
})); // NULL
 
// Find the first animal where the array key is the first symbol of the animal.
var_dump(array_find($array, function (string $value, $key) {
   return $value[0] === $key;
})); // string(3) "cow"
 
// Find the first animal where the array key matching a RegEx.
var_dump(array_find($array, function ($value, $key) {
   return preg_match('/^([a-f])$/', $key);
})); // string(3) "dog"

array_find_key

array_find_key returns the key of the first element for which the $callback returns true. If no matching element is found the function returns NULL.

function array_find_key(array $array, callable $callback): mixed {
    foreach ($array as $key => $value) {
        if ($callback($value, $key)) {
            return $key;
        }
    }
 
    return null;
}

Parameters

array $array

The array that should be searched.

callable $callback

The callback function to call to check each element. The first parameter contains the value, the second parameter contains the corresponding key. If this function returns true, the key is returned from array_find_key and the callback will not be called for further elements.

Return Value

The function returns the key of the first element for which the $callback returns true. If no matching element is found the function returns NULL.

Examples

$array = [
    'a' => 'dog',
    'b' => 'cat',
    'c' => 'cow',
    'd' => 'duck',
    'e' => 'goose',
    'f' => 'elephant'
];
 
// Find the first animal with a name longer than 4 characters.
var_dump(array_find_key($array, function (string $value) {
    return strlen($value) > 4;
})); // string(1) "e"
 
// Find the first animal whose name begins with f.
var_dump(array_find_key($array, function (string $value) {
    return str_starts_with($value, 'f');
})); // NULL
 
// Find the first animal where the array key is the first symbol of the animal.
var_dump(array_find_key($array, function (string $value, $key) {
   return $value[0] === $key;
})); // string(1) "c"
 
// Find the first animal where the array key matching a RegEx.
var_dump(array_find_key($array, function (string $value, $key) {
   return preg_match('/^([a-f])$/', $key);
})); // string(1) "a"

array_any

array_any returns true, if $callback returns true for any element. Otherwise the function returns false.

function array_any(array $array, callable $callback): bool {
    foreach ($array as $key => $value) {
        if ($callback($value, $key)) {
            return true;
        }
    }
 
    return false;
}

Parameters

array $array

The array that should be searched.

callable $callback

The callback function to call to check each element. The first parameter contains the value, the second parameter contains the corresponding key. If this function returns true, true is returned from array_any and the callback will not be called for further elements.

Return Value

The function returns true, if there is at least one element for which $callback returns true. Otherwise the function returns false.

Examples

$array = [
    'a' => 'dog',
    'b' => 'cat',
    'c' => 'cow',
    'd' => 'duck',
    'e' => 'goose',
    'f' => 'elephant'
];
 
// Check, if any animal name is longer than 5 letters.
var_dump(array_any($array, function (string $value) {
    return strlen($value) > 5;
})); // bool(true)
 
// Check, if any animal name is shorter than 3 letters.
var_dump(array_any($array, function (string $value) {
    return strlen($value) < 3;
})); // bool(false)
 
// Check, if any array key is not a string.
var_dump(array_any($array, function (string $value, $key) {
   return !is_string($key);
})); // bool(false)

array_all

array_all returns true, if $callback returns true for all elements. Otherwise the function returns false.

function array_all(array $array, callable $callback): bool {
    foreach ($array as $key => $value) {
        if (!$callback($value, $key)) {
            return false;
        }
    }
 
    return true;
}

Parameters

array $array

The array that should be searched.

callable $callback

The callback function to call to check each element. The first parameter contains the value, the second parameter contains the corresponding key. If this function returns false, false is returned from array_all and the callback will not be called for further elements.

Return Value

The function returns true, if $callback returns true for all elements. Otherwise the function returns false.

Examples

$array = [
    'a' => 'dog',
    'b' => 'cat',
    'c' => 'cow',
    'd' => 'duck',
    'e' => 'goose',
    'f' => 'elephant'
];
 
// Check, if all animal names are shorter than 12 letters.
var_dump(array_all($array, function (string $value) {
    return strlen($value) < 12;
})); // bool(true)
 
// Check, if all animal names are longer than 5 letters.
var_dump(array_all($array, function (string $value) {
    return strlen($value) > 5;
})); // bool(false)
 
// Check, if all array keys are strings.
var_dump(array_all($array, function (string $value, $key) {
   return is_string($key);
})); // bool(true)

Backward Incompatible Changes

Functions created by the user and named array_find, array_find_key, array_any or array_all lead to a PHP error with the new version. A quick GitHub search shows, that there a 656 results defining the symbol array_find, 28 results defining the symbol array_find_key, 127 results defining the symbol array_any and 284 results defining the symbol array_all for the language PHP.

Looking at the search results I estimate about 30% of these results are functions that are not located in a namespace, are not part of a class and are not false-positvies (e.g. the symbol is db_array_all instead of array_all).

Proposed PHP Version(s)

PHP 8.4

RFC Impact

To SAPIs

None.

To Existing Extensions

None.

To Opcache

None.

New Constants

None.

php.ini Defaults

None.

Open Issues

This function behaves differently to array_filter, because it always passes the value and the key to the callback. array_filter has a parameter for this to set which parameters are to be passed to the callback. array_map does not support the pass of the key at all, but support using more than one array, to pass a various number of parameters to the callback. With closures, omitting a parameter is now possible without any problems, therefore, there is no reason to determine the choice of parameters for the callback.

Unaffected PHP Functionality

This RFC only adds two new functions to PHP and only affects previously defined functions which are named as the proposed functions.

Proposed Voting Choices

Each vote requires a 2/3 majority.

Voting started on 2024-05-15 and will end on 2024-05-29 15:00 UTC.

array_find()/array_find_key()

Add array_find() and array_find_key()?
Real name Yes No
alcaeus (alcaeus)  
ashnazg (ashnazg)  
beberlei (beberlei)  
cpriest (cpriest)  
crell (crell)  
derick (derick)  
galvao (galvao)  
jimw (jimw)  
josh (josh)  
kguest (kguest)  
mbeccati (mbeccati)  
mfonda (mfonda)  
nielsdos (nielsdos)  
petk (petk)  
ramsey (ramsey)  
seld (seld)  
sergey (sergey)  
timwolla (timwolla)  
weierophinney (weierophinney)  
Count: 19 0

array_any()/array_all()

Add array_any() and array_all()?
Real name Yes No
alcaeus (alcaeus)  
ashnazg (ashnazg)  
cpriest (cpriest)  
crell (crell)  
derick (derick)  
galvao (galvao)  
jimw (jimw)  
josh (josh)  
kguest (kguest)  
mbeccati (mbeccati)  
mfonda (mfonda)  
nielsdos (nielsdos)  
petk (petk)  
ramsey (ramsey)  
seld (seld)  
sergey (sergey)  
timwolla (timwolla)  
weierophinney (weierophinney)  
Count: 18 0

Implementation

References

rfc/array_find.txt · Last modified: 2024/05/15 14:55 by josh