Using cloures to pass additional data to callbacks
Now that we’ve seen how to create a closure, let’s look at a common practical use for them.
When you pass a callback function to the PHP
usort()
function, and usort()
calls your callback, the callback receives the two arguments passed to it by usort()
— that is, the two values in the array to compare.
But what if you wanted your callback function to receive extra information? For example, taking our
usort()
example from earlier in the article, we might want to pass an additional $sortKey
argument to our callback to tell it which key it should use for sorting the array ("name"
or "age"
).
However,
usort()
— not us — calls our callback. Since it’s called within usort()
‘s scope, we don’t have a chance to pass additional arguments to the callback at the time it’s called.
Closures to the rescue! By returning our callback function from inside another function and creating a closure, we can get the outer function to accept
$sortKey
as a parameter, then pass $sortKey
to the callback inside the closure. Here’s the complete code:$people = array( array( "name" => "Fred", "age" => 39 ), array( "name" => "Sally", "age" => 23 ), array( "name" => "Mary", "age" => 46 ) ); function getSortFunction( $sortKey ) { return function( $personA, $personB ) use ( $sortKey ) { return ( $personA[$sortKey] < $personB[$sortKey] ) ? -1 : 1; }; } echo "Sorted by name:<br><br>"; usort( $people, getSortFunction( "name" ) ); print_r( $people ); echo "<br>"; echo "Sorted by age:<br><br>"; usort( $people, getSortFunction( "age" ) ); print_r( $people ); echo "<br>";
This code displays the expected output:
Sorted by name: Array ( [0] => Array ( [name] => Fred [age] => 39 ) [1] => Array ( [name] => Mary [age] => 46 ) [2] => Array ( [name] => Sally [age] => 23 ) ) Sorted by age: Array ( [0] => Array ( [name] => Sally [age] => 23 ) [1] => Array ( [name] => Fred [age] => 39 ) [2] => Array ( [name] => Mary [age] => 46 ) )
Let’s walk through this code to see how it works:
- Create an array of people.
First we create our usual array of people. Each person is represented by an associative array with two keys:"name"
and"age"
. - Define
getSortFunction()
.
Next we define a regular function,getSortFunction()
, that accepts a$sortKey
parameter. This is the key that we want our sorting callback function to use. WithingetSortFunction()
, we… - Create the closure.
On lines 8-10,getSortFunction()
defines and returns an anonymous function, which is the sorting function that we’ll pass tousort()
. The anonymous function accepts the usual two array elements to sort —$personA
and$personB
— but it also uses theuse
keyword to access the$sortKey
parameter, which is within the scope of the enclosinggetSortFunction()
function. This creates the closure. It then uses$sortKey
to determine which key to use for the comparison (either"name"
or"age"
).Remember: Since we’ve created a closure, the anonymous function still has access to the value of the$sortKey
parameter aftergetSortFunction()
has finished running. - Sort the array.
Finally our code sorts the array, first by name and then by age. To do this, it callsgetSortFunction()
, passing in the key to sort by ("name"
or"age"
).getSortFunction()
then returns an appropriate anonymous sort function that uses the supplied sort key. The code then passes this anonymous function tousort()
, along with the array to sort.
You can use this trick any time you need to pass additional data to a callback function.
Closures are quite a broad and subtle topic. If you’re not familiar with them, check out the following good articles on anonymous functions and clousers in PHP and cloures and lambda functions, which explain in detail exactly what a closure is (and isn’t).
No comments:
Post a Comment