What is a Lambda Function
A lambda function (also known as anonymous function ) is a function that has:
- No Name
- Can be assigned to a variable
- Can be passed as argument to other functions or methods of a class
In the past, this was possible with create_function
like:
$multiply = create_function('$a, $b','return $a * $b;');
echo $multiply(5, 5); // 25
But it was a nuisance if you needed to write longer anonymous functions using create_function
because it was difficult to write function body that way inside quotes and then escaping quotes and stuff...
PHP 5.3 introduced true support of lambda/anonymous functions similar to how you see in Javascript, so you could write above code like:
$multiply = function ($a, $b) {
return $a * $b;
};
echo $multiply(5, 5); // 25
As can be seen, above anonymous function has no name and has been assigned to variable $multiply
. You can also pass it other functions:
function countEmails($number) {
echo 'you have ' . $number . ' emails!';
}
$multiply = function ($a, $b) {
return $a * $b;
};
echo countEmails($multiply(2, 5)); // you have 10 emails!
You could have also written above like this if you wanted:
function countEmails($multiply, $a, $b) {
echo 'you have ' . $multiply($a, $b) . ' emails!';
}
$multiply = function ($a, $b) {
return $a * $b;
};
echo countEmails($multiply, 2, 5); // you have 10 emails!
And even like this:
function countEmails($multiply, $a, $b) {
echo 'you have ' . $multiply($a, $b) . ' emails!';
}
echo countEmails(function ($a, $b) {
return $a * $b;
}, 2, 5); // you have 10 emails!
You can also assign it to array:
$array['multiply'] = function ($a, $b) {
return $a * $b;
};
echo $array['multiply'](2, 10); // 20
And even an object:
$obj = new StdClass();
$obj->multiply = function ($a, $b) {
return $a * $b;
};
But caveat here is that instead of calling it like echo $obj->multiply(2, 10)
, you would instead need to do:
$multiply = $obj->multiply;
echo $multiply(2, 10); // 20
Now let's create little program which says Hello, World
using anonymous function:
$message = 'Hello, World!';
$sayHello = function () {
echo $message;
};
$sayHello();
Oops, it results in error:
Notice: Undefined variable: message
It means anonymous function does not have access to $message
inside it. This is exactly when we need a closure to have access to that $message
variable.
What is a Closure
A closure is a lambda/anonymous function that is aware of its surrounding context through the use of use
keyword. It can access a variable outside of the scope where it is defined. Consider last $sayHello
anonymous function and we convert it into closure through the use of use
keyword and gain access to the $message
variable:
$message = 'Hello, World!';
$sayHello = function () use ($message) {
echo $message;
};
$sayHello(); // Hello, World!
Thanks to closure (use
keyword), we are now able to access $message
variable!
If you need to change the value of passed outside variable, you need to use reference of course:
$message = 'Hello, World!';
$sayHello = function () use (&$message) {
echo $message;
$message = 'Hello World Again!';
};
$sayHello(); // Hello, World!
$sayHello(); // Hello World Again!
You can also pass regular arguments to closure:
$message = 'World!';
$sayHello = function ($arg) use ($message) {
echo $arg . ' ' . $message;
};
$sayHello('Hello'); // Hello, World!
You can also create a recursive function using closure easily, here is example of how you can create a factorial function:
$factorial = function( $n ) use ( &$factorial ) {
if( $n == 1 ) return 1;
return $factorial( $n - 1 ) * $n;
};
echo $factorial( 5 );
Notice that $factorial
is passed by reference using &
otherwise it won't work.
The difference between an anonymous function and closure is that closure has ability to access a variable outside of its scope by using the use
keyword. So that is the subtle difference between an anonymous function and a closure. In fact, both are of them are instances of Closure
class internally:
// anonymous function
$multiply = function ($a, $b) {
return $a * $b;
};
$message = 'Hello, World';
// closure
$sayHello = function () use ($message) {
echo $message;
};
var_dump ($multiply instanceof Closure); // true
var_dump ($sayHello instanceof Closure); // true
Type Hinting
We know that you can already type hint few things:
- Object
- Array
- Interface
- callable
But you can also type hint Closure
as shown below:
function multiply($number, Closure $closure)
{
return $closure($number);
}
$closure = function($x){ return $x * 2; };
echo multiply(10, $closure);
Here we have made multiply
function to require type of Closure
as second parameter.
Use Cases
Anonymous functions and closures can be used in variety of situations.
As Callbacks
You can use them in your custom functions as callback or some of the PHP's built-in functions such as array_map()
, array_reduce()
, array_filter()
, array_walk()
, etc. Let's take example of array_walk()
. If you look at it's definition, here is how it looks:
bool array_walk ( array &$array , callable $callback [, mixed $userdata = NULL] )
Notice the second parameter callable $callback
. It means it expects something to be callable function. Anytime you see such parameter in some function definition, it means you can apply anonymous functions to it. Here is example:
$myArray = array(1, 2, 3, 4, 5);
// multiply each array value with 2
array_walk($myArray, function(&$value, $index){
$value *= 2;
});
print_r($myArray);
Array
(
[0] => 2
[1] => 4
[2] => 6
[3] => 8
[4] => 10
)
In Routing
You might have seen closures being used in routing in framework like Laravel, Slim, Silex, etc:
$app = new \Slim\Slim($options);
$app->get('/', function () use ($app) {
$app->render('home', array('content' => 'Hello, World!!!'));
});
Here $app
is imported from outside scope into the scope of closure so that it can be used to render the view $app->render(...)
Accessing Private Members of a Class
We can use bind()
or bindTo
methods of Closure
class to access private
data of some class, for example:
class MyClass {
private $variable = 'I am private variable!';
}
$closure = function() {
return $this->variable;
};
$result = Closure::bind($closure, new MyClass(), 'MyClass');
echo $result(); // I am private variable!
There you see, we were able to get value of private variable $variable
.
Similarly, it is also possible to add new behaviour to a class without actuallly modifying it direclty. Pretty cool hun ?
Lazy-loading a Class
Another ^cool^
thing you can do with closure is to lazy-load a class. For example:
class MyClass {
public function __construct() {
echo 'I am initialized!';
}
}
$getMyClass = function() {
$myClass = new MyClass();
return $myClass;
};
If you run above code, you might expect to see I am initialized!
message because one might think we are creating an instance of MyClass
. That's not true though, because code inside $getMyClass
anonymous function is not run until you actually call it:
$myClass = $getMyClass();
And now you would see the message I am initialized!
. So this is pretty neat trick to defer some piece of code and use it only when you need it.
Top comments (0)