DEV Community

Unpublished Post. This URL is public but secret, so share at your own discretion.

PHP RFC: array_filter_list() function

Introduction

Function array_is_list() introduced in PHP 8.1 differentiates "array lists" whose indexes are numeric 0..count-1 from associative arrays and arrays having irregular indexes.

Array filtering using array_filter() does work on all arrays, but it exhibits a nuanced behavior on lists leading to subtle bugs. For instance, it preserves keys which can create gaps in sequential indexes. In such cases, a filtered list becomes not a list anymore, which is counterintuitive and can be considered an unintended behavior most of the time.

For example:

$originalList = ['first', '', 'last'];
$filteredList = array_filter($originalList);

var_export(filteredList);  // array(0 => 'first', 2 => 'last')
var_export(array_is_list($originalList));  // true
var_export(array_is_list($filteredList));  // false
Enter fullscreen mode Exit fullscreen mode

Furthermore, this can cause unexpected encoding issues:

echo json_encode($originalList);  // ["first", "", "last"]
echo json_encode($filteredList);  // {"0": "first", "2": "last"}
Enter fullscreen mode Exit fullscreen mode

It's very easy to overlook this behavior. The issue can remain unnoticed on data that contains only trailing items to be filtered out. Even automated tests could be passing, unless they test for the gaps specifically.

Workaround

There is a way to code around this – reset indexes using array_values().

Boilerplate:

$filteredList = array_values(array_filter($originalList));

var_export(filteredList);  // array(0 => 'first', 1 => 'last')
var_export(array_is_list($filteredList));  // true
echo json_encode($filteredList);  // ["first", "last"]
Enter fullscreen mode Exit fullscreen mode

The workaround is verbose, it hinders readability, and has an unnecessary performance impact (albeit usually negligible) of having to re-build the array with 0..count-1 indexes.

Proposal

The solution is to introduce a new function array_filter_list() having the same signature as array_filter(), but working on lists specifically.

$filteredList = array_filter_list($originalList);
Enter fullscreen mode Exit fullscreen mode

The behavior is equivalent to the following:

function array_filter_list(array $array, ?callable $callback = null, int $mode = 0): array
{
    if (!array_is_list($array)) {
        throw new ValueError('Array list expected');
    }
    return array_values(array_filter($array, $callback, $mode));
}
Enter fullscreen mode Exit fullscreen mode

Internal implementation should be optimized to avoid rebuilding array indexes from scratch.

Backward Compatibility

The function name array_filter_list() will be reserved. Any codebase declaring such a function will fail with the following error:

PHP Fatal error: Cannot redeclare array_filter_list()

There are only 4 mentions of this function on GitHub all belonging to the same project Froq! Hassle-free PHP framework which is not mainstream.

Top comments (0)