phparraysenumsarray-difference

Why doesn't array_diff() work straight away with an array of enums, whereas in_array() or array_udiff() do?


Using PHP 8.4, given the following functions:

function test_array_diff($all, $filtered)
{
    return array_diff($all, $filtered);
}
function test_array_udiff($all, $filtered)
{
    return array_udiff($all, $filtered, fn($item1, $item2) => $item1 != $item2);
}
function test_in_array($all, $filtered)
{
    $diff = [];

    foreach ($all as $case) {
        if (!in_array($case, $filtered, true)) {
            $diff[] = $case;
        }
    }

    return $diff;
}

The following enum:

enum MyEnum
{
    case FOO;
    case BAR;
}

And those variables:

$all = MyEnum::cases();
$filtered = [MyEnum::BAR];

Why do subsequent calls work as expected:

var_dump(test_array_udiff($all, $filtered));
var_dump(test_in_array($all, $filtered));

But this one throws:

var_dump(test_array_diff($all, $filtered));

Fatal error: Uncaught Error: Object of class MyEnum could not be converted to string

Shouldn't array_diff() be able to work "out of the box" with enum arrays in the same way as in_array() or array_udiff()?


Solution

  • From the documentation of array_diff():

    Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. That is, when the string representation is the same.

    Since enums can't be converted to strings, this test is not possible.

    The equivalent test_udiff() would be:

    function test_array_udiff($all, $filtered)
    {
        return array_udiff($all, $filtered, fn($item1, $item2) => (string)$item1 <=> (string)$item2);
    }
    

    Note also that you should use the <=> comparison operator. The callback for array_udiff should return a negative, positive, or zero value to indicate the relationship between the values, not a boolean.