DEV Community

Sarfraz Ahmed
Sarfraz Ahmed

Posted on • Originally published at codeinphp.github.io on

Exploring Lambda Functions and Closures in PHP

What is a Lambda Function

A lambda function (also known as anonymous function ) is a function that has:

  1. No Name
  2. Can be assigned to a variable
  3. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

You can also assign it to array:

$array['multiply'] = function ($a, $b) {
     return $a * $b;
};

echo $array['multiply'](2, 10); // 20
Enter fullscreen mode Exit fullscreen mode

And even an object:

$obj = new StdClass();

$obj->multiply = function ($a, $b) {
     return $a * $b;
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now let's create little program which says Hello, World using anonymous function:

$message = 'Hello, World!';

$sayHello = function () {
    echo $message;
};

$sayHello();
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

You can also pass regular arguments to closure:

$message = 'World!';

$sayHello = function ($arg) use ($message) {
    echo $arg . ' ' . $message;
};

$sayHello('Hello'); // Hello, World!
Enter fullscreen mode Exit fullscreen mode

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 );
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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] )
Enter fullscreen mode Exit fullscreen mode

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
)
Enter fullscreen mode Exit fullscreen mode

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!!!'));
});
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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)