Possible to pass a closure to usort in PHP?
Asked Answered
E

2

7

I have an array sorting function as follows:

public function sortAscending($accounts)
{
    function ascending($accountA, $accountB) {
        if ($accountA['AmountUntilNextTarget'] == $accountB['AmountUntilNextTarget']) {
            return 0;
        }
        return ($accountA['AmountUntilNextTarget'] < $accountB['AmountUntilNextTarget']) ? -1 : 1;
    }
    usort($accounts, $ascending);

    return $accounts;
}

Clearly this is not ideal as it is hard-coding the key to search for. I thought I would make this generic by passing the key as a param to outside function, however this is then out-of-scope in the inner function. I tried to get around this by using a closure, which would have access to the param, instead of an inner function as follows:

public function sortAscending($accounts, $key)
{
    $ascending = function($accountA, $accountB) {
        if ($accountsA[$key] == $accountB[$key]) {
            return 0;
        }
        return ($accountA[$key] < $accountB[$key]) ? -1 : 1;
    }
    usort($accounts, $ascending);

    return $accounts;
}

However usort() only accepts a function name, so this doesn't work. Can anyone see a (better?) way of achieving this?

Espresso answered 11/5, 2011 at 15:25 Comment(2)
usort accepts a callback parameter, which can be a closure. The obvious error in your code is the missing ; after the function definition. Maybe that is the cause of the error.Applesauce
Apart from a missing ;, $key is also not in the scope of your closure. You should use it: $ascending = function($acciontA, $accountB) use($key) { ...Claustrophobia
W
15

Closures may also inherit variables from the parent scope. Any such variables must be declared in the function header. Inheriting variables from the parent scope is not the same as using global variables. Global variables exist in the global scope, which is the same no matter what function is executing. The parent scope of a closure is the function in which the closure was declared (not necessarily the function it was called from).

  • Please also note that defining a closure and assigning it to a variable is a normal assignment operation, so you will need the ; after the closing } of the closure.

After making these changes your code would look like this (and should work fine):

public function sortAscending($accounts, $key)
{
    $ascending = function($accountA, $accountB) use ($key) {
        if ($accountsA[$key] == $accountB[$key]) {
            return 0;
        }
        return ($accountA[$key] < $accountB[$key]) ? -1 : 1;
    };
    usort($accounts, $ascending);

    return $accounts;
}
Washwoman answered 11/5, 2011 at 15:48 Comment(1)
Thanks bazmegakapa and others for your quick reply, this is really helpful. Apologies for the missing semi-colon I hadn't picked up on that, school boy error.Espresso
S
6

To clarify this - and code it like a closure - and use the PHP7 spaceship operator - and correct a typo in line 4 of the first answer, skip the $ascending variable:

public function sortAscending($accounts, $key)
{
    usort($accounts, function($accA, $accB) use ($key) {
        return $accA[$key] <=> $accB[$key];
    });
    return $accounts;
}
Stanley answered 25/2, 2019 at 22:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.