PHP Functions - Closures

Chapter 35 29 mins

Learning outcomes:

  1. What are anonymous functions
  2. Creating anonymous functions in PHP
  3. What are closures and the Closure class
  4. Using outer variables via the use keyword
  5. More about the use keyword

What are anonymous functions?

Many programming languages in use today support the notion of anonymous functions out of the box, sometimes also referred to as lambda functions. PHP is amongst this group of languages.

So what is an anonymous function?

Well, speaking in terms of programming:

An anonymous function is a function without a name.

That's precisely and solely what an anonymous function is.

The idea of lambda functions

As stated earlier, anonymous functions sometimes go by the name lambda functions.

If you're curious about the origin of the term 'lambda' here, the idea of lambda functions in programming was actually borrowed from a similar idea in mathematics, particularly in lambda calculus.

Now you might be thinking why would anyone ever need a function without a name? Well, there might be some cases where this applies.

Recall the array_map() function from the PHP Arrays — Functions chapter. Let's say you want to map an array of integers to an array of the squares of those integers.

One way is to obviously use a named function as we did in that chapter:

<?php

function to_square($num) {
   return $num ** 2;
}

$nums = [10, 12, -5, -3, 0];
$squares = array_map('to_square', $nums);

print_r($squares);
Array ( [0] => 100 [1] => 144 [2] => 25 [3] => 9 [4] => 0 )

However, if you think about it for a while, creating a named function for this task is more-or-less an overkill. We ideally want a quick and easy way to specify the mapping rule of squaring an element, without having to worry much about the name of the function defining this rule.

What if the rule becomes an extremely complex one? What will we name the function then?

Moreoever, we don't even need to pollute the global namespace with a function that's only meant to be called by array_map(), and possibly only once.

What we really want here is an anonymous function.

Let's now see how to create anonymous functions in PHP.

Creating anonymous functions

We've already seen how to create an anonymous function in the PHP Functions — Basics chapter.

It's really simple. Everything is the same as in defining a named function in PHP except for one thing: just omit the name of the function. And now what you have behind is an anonymous function.

Here's the syntax of an anonymous function:

function($param_1, $param_2, ..., $param_n) {
   ...
}

One important thing to note here is that, unlike named function definitions in PHP (which otherwise denote statements), anonymous function definitions denote expressions.

Hence, wherever PHP expects an expression, an anonymous function can be provided there.

In fact, this is a feature of anonymous functions: to act as expressions so that it's super convenient for us to directly provide them to functions, as callbacks, and return them from functions (the most common uses of anonymous functions).

Let's now define some anonymous functions.

Below, we rewrite the array_map() code above, utilizing an anonymous function to define the mapping rule instead of this being done by a named function:

<?php

$nums = [10, 12, -5, -3, 0];
$squares = array_map(function($num) { return $num ** 2; }, $nums);

print_r($squares);
Array ( [0] => 100 [1] => 144 [2] => 25 [3] => 9 [4] => 0 )

Notice how much simplified this code is compared to the previous one. There is no need to first create the function and then pass its name to array_map(), and neither is there a name in the global context that's not supposed to be there.

Over to example number two.

In the code below, we have a function get_greeting_fn() that takes in an argument specifying whether we are greeting a programmer or a teacher and then returns back an appropriate function to make the actual greeting:

<?php

function get_greeting_fn($type = null) {
   if ($type === 'programmer') {
      return function($name) {
         echo "Hello programmer $name.\n";
      };
   }
   elseif ($type === 'teacher') {
      return function($name) {
         echo "Hello teacher $name.\n";
      };
   }
   else {
      return function($name) {
         echo "Hello $name.\n";
      };
   }
}

The returned function itself takes the name of the person to greet and, when called, outputs the greeting message to the terminal.

Let's get some greeting functions and call them to make a couple of greetings:

<?php

function get_greeting_fn($type = null) {
   /* ... */
}

$greet_programmer = get_greeting_fn('programmer');
$greet_programmer('Alice');
$greet_programmer('Bob');
echo "\n";

$greet_teacher = get_greeting_fn('teacher');
$greet_teacher('Alice');
$greet_teacher('Bob');
echo "\n";

$greet = get_greeting_fn();
$greet('Alice');
$greet('Bob');
Hello programmer Alice. Hello programmer Bob. Hello teacher Alice. Hello teacher Bob. Hello Alice. Hello Bob.

$greet_programmer holds a function meant to greet programmers, $greet_teacher holds a function meant to greet teachers, and $greet holds a function meant to greet just about anyone. We call each of these functions twice, with a given set of names, in order to greet given people.

The point to note here is that by virtue of returning a function from get_greeting_fn(), we are able to customize how the greeting essentially happens.

While this same thing could've been accomplished by using a single generic greet_person() function having two parameters: the first one representing the type of person to greet and the second one representing that person's name, using a function that returns a concrete greeting function has the benefit that we don't have to provide the type of the person again and again.

For example, imagine we have a list of 100 teachers to greet. We'd be better off at using a function that knows that we are greeting teachers (like $greet_teacher() above) than using a generic function where we ought to provide this information with each invocation.

Even though this example, and the one shown above, may not be that practical, it does illustrate a common pattern used in complex PHP programs, i.e. functions returning other functions that respect some of the configurations we pass on while calling the outer functions.

Higher-order functions

A function that accepts in a function as argument or returns a function, or does both of them, is referred to as a higher-order function.

So in the code above, get_greeting_fn() is a higher-order function as it returns a function. However, the anonymous function returned by get_greeting_fn() is not a higher-order function since it neither accepts a function as argument, nor does it return a function.

Moving on, if you notice one thing in the get_greeting_fn() function above, there is an unnecessary amount of repetition involved in it. In each conditional statement, we return more-or-less the same kind of function: it takes in a name and prints a greeting message.

We could largely simplify this code, removing all the conditional statements and reducing down to just one single return. But this could only be done once we understand the concept of closures in PHP.

What are closures?

Anonymous functions are functions without a name. Period. This doesn't, however, tell us anything about how the functions interact with their surrounding environment, i.e. are they able to access variables from the surrounding environment.

That aspect essentially boils down to how the programming language implements anonymous functions and what bells and whistles it gives to them.

Talking about PHP, it implements anonymous functions as closure functions, or simply closures.

A function is said to be a closure if it somehow remembers its outer variables.

A closure is basically a function that is able to remember variables from its enclosing environment.

Quite often, people talk about the 'remembrance' factor of closures as follows: a function f() has a local variable $x and returns another function. If that returned function is a closure, then it'll be able to read and write to the variable $x from its outer environment, that is, the local environment of f().

In PHP, all anonymous functions are closures and, conversely, all closures are anonymous functions. Therefore, the two terms are interchangeable in PHP parlance.

Even though the terms 'anonymous function' and 'closure' are interchangeable in PHP, it's important to keep in mind that they technically represent two disparate concepts.

If we were to be really precise, anonymous functions in PHP are instances of the built-in Closure class.

To check if an arbitrary callable value is a closure (or identically, an anonymous function), we can just check if it's an instance of the Closure class.

An example is illustrated as follows:

<?php

$value = function() {};
var_dump($value instanceof Closure);
bool(true)

This example uses ideas in PHP that are tied to working with objects and classes — in particular, the instanceof operator. We'll learn more about them in the PHP OOP unit.

A closure really isn't a closure until it accesses a variable from its enclosing environment. After all, this is the real essence of closures — to keep track of their enclosing environments (a thing which couldn't be done by named functions in PHP).

But the problem here is that a closure function in PHP can't access anything until we explicitly allow it to access that thing. This is done using the use keyword.

Consider the following code:

<?php

function f() {
   $x = 10;
   return function() {
      echo $x;
   };
}

$func = f();
$func();

We have a function f() returning a closure that tries to access the local variable $x of f() and return it. The function f() is called and then the function it returns.

Surprsingly enough, when we run this code, we get the following warning:

Warning: Undefined variable $x in <file> on line 6

The variable $x in the statement echo $x, in the anonymous function, doesn't seem to exist.

We constantly shift between the terms 'closure' and 'anonymous function' in this discussion in order to emphasize the fact that they are interchangeable in PHP.

But why is this so?

Well, by design, and by default, PHP does NOT allow a closure access to any outer variable. That's why when we access $x from within the closure above, PHP complains about a non-existent variable.

If we want to give the closure access to a given outer variable, or a set of outer variables, we ought to do so explicitly, using the use keyword.

Here's the syntax of use when used with a closure:

function(...) use ($var_1, $var_2, ..., $var_n) {
   ...
}

The use keyword comes after the parameter list in the anonymous function's definition, followed by a list of variables to access from the outer scope. Simple.

Let's solve the issue in the code above by allowing the closure access to the outer variable $x:

<?php

function f() {
   $x = 10;
return function() use ($x) { echo $x; }; } $func = f(); $func();

Time to test the code:

10

And there you have it. The closure can now access the local variable $x of f(), all thanks to use. Amazing.

Let's now consider a more involved example.

Recall the get_greeting_fn() function from the section above, returning a different anonymous function based on the type of person provided to it. Also recall how much repetition was going on in its definition above.

Now, it's time to improve the implementation of get_greeting_fn() using the power of closures.

Here's the new definition of get_greeting_fn():

<?php

function get_greeting_fn($type = null) {
   return function($name) use ($type) {
      echo "Hello $type $name.\n";
   };
}

As promised earlier, by leveraging the fact that the returned function here is a closure, we've effectively cleaned up the get_greeting_fn() to a great extent. The conditionals are gone and we now have just one return.

It can't get any better!

Now, as always, let's test the function:

<?php

function get_greeting_fn($type = null) {
   /* ... */
}

$greet_programmer = get_greeting_fn('programmer');
$greet_programmer('Alice');
$greet_programmer('Bob');
echo "\n";

$greet_teacher = get_greeting_fn('teacher');
$greet_teacher('Alice');
$greet_teacher('Bob');
echo "\n";

$greet = get_greeting_fn();
$greet('Alice');
$greet('Bob');
Hello programmer Alice. Hello programmer Bob. Hello teacher Alice. Hello teacher Bob. Hello Alice. Hello Bob.

Yes, we indeed get the desired output, however the last set of outputs has an odd spacing. This is because of the way $type is interpolated in the given template string.

In the code that follows we solve this issue using a simple trick:

<?php

function get_greeting_fn($type = null) {
$type = $type ? $type . ' ' : ''; return function($name) use ($type) {
echo "Hello $type$name.\n"; }; } /* ... */

We add a space at the end of $type if it is a string, otherwise make it an empty string. And then, in the string, we interpolate $type right before the $name variable.

Hello programmer Alice. Hello programmer Bob. Hello teacher Alice. Hello teacher Bob. Hello Alice. Hello Bob.

Perfect!

More about use

The use keyword's semantics when used with anonymous functions are not really that difficult to understand but they sure have many aspects to them which we didn't cover in the section above for brevity. We'll now do so in this very section.

Variables are used by-value, by default

To start off, note that whatever variables are passed on to an anonymous function via use are by value, not by reference, unless we explicitly do so.

Hence, if we have a variable assignment statement in an anonymous function, accessing an outer variable, it won't actually end up modifying that outer variable. It's quite easy to verify this behavior.

Below, we create a function f() with a static local variable $x and return a closure from it, which, when called, would modify this variable $x:

<?php

function f() {
   static $x = 10;
   echo "\$x: $x\n";

   return function() use ($x) {
      $x = 50;
   };
}

Let's run f() and then the returned function, followed by calling f() another time in order to see the value of its local variable $x:

<?php

function f() {
   static $x = 10;
   echo "\$x: $x\n";

   return function() use ($x) {
      $x = 50;
   };
}

$func = f();
$func();

// Call f() again to see the value of $x.
f();
$x: 10 $x: 10

As can be seen in the output, the static local variable's value remains the same across both the calls to the function f().

So one thing that's clear is that passing a variable normally using use and then modifying it inside the anonymous function has absolutely no effect on the original variable.

It's worthwhile mentioning here that the variable assignment statement $x = 50 inside the closure above (since it obviously doesn't apply to the outer $x variable) is merely creating a local variable $x, initialized to 50.

Variables are captured upon the closure's definition

There is an aspect of closure functions which can be easily reasoned by the assumption that closure functions create and maintain internal slots, storing the values of variables at the site of their definition.

That aspect is detailed as follows:

<?php

function f() {
   $x = 10;

   $func = function() use $x {
      echo $x;
   };

   $x = 50;
   return $func;
}

We have the same setup as before except for two things. The anonymous function is not returned right away; instead, it's created and then the value of $x mutated in f() before finally getting to the return of the closure.

Now, before we get to execute this code, we encourage you to think about what would $x be in the closure. 10 or 50?

Follow your natural intuition to answer this question. Once done, continue on reading below.

If you answered 50, congratulations! You indeed thought the way anyone, be it a newbie or someone experienced with closures beforehand from another language, would have thought about this code. Unfortunately, it's the wrong answer!

Let's see how and why.

A closure in PHP takes up the values of all used variables right at the site of its definition and stores them in internal slots.

If we thereafter modify those variables outside the closure, the closure won't bother to update its internal slots. This is because it does not store the variables by reference, by default, even though we can do so (more on that later below).

Saving variables at site of definition does make some sense!

The fact that a closure in PHP captures all the variables given by use right at the instant of its definition seems to be a fair behavior since a closure in PHP is an instance of the Closure class.

An instance, when it's being instantiated, has to be given all the necessary pieces of data for it to work; and for an instance of Closure, this data is the set of variables to save from the outer environment.

The moment a closure is defined, it's actually equivalent to the Closure class being instantiated. During this process, the closure instance stores the current values of all the used variables in it. Thereafter, if we change any of those variables in the outer environment directly, the closure won't pick up the changes because its internal slots have already been finalized with the respective values.

If you don't have experience of object-oriented programming, there's no need to worry. We'll cover OOP in the next unit and once you learn that, you can review this chapter to get a better sense of closures and the Closure class.

Hence, in the code above, $x inside the closure would be 10. Now, as you would probably be thinking about this and saying that 'this is counter-intuitive,' well, to some extent, yes it sure is so.

But if we think about the whole design philosophy of PHP and that functions are only meant to have local variables, it does seem to align with that design philosophy. There's an interesting discussion of this aspect, amongst many others, of closures at RFC: Lambda functions and closures — wiki.php.net

Variables can be used by-reference

Anyways, coming back to the use keyword, if it's desired to be able to modify outer variables from a closure, what we need is...you guessed it: references.

To get a refresher on references, visit the PHP References chapter of this course.

Just like we can precede parameters in a parameter list in a function's definition to get them by reference, we can do so in the list of variables following the use keyword.

Syntactically, it looks similar to the following:

function(...) use (&$param_1, ...) {}

Let's consider a quick example.

In the following code, we rewrite the static local variable example above, leveraging a pass-by-reference used variable in order to be able to mutate the static variable from within the closure:

<?php

function f() {
   static $x = 10;
   echo "\$x: $x\n";

   return function() use (&$x) {
      $x = 50;
   };
}

$func = f();
$func();

// Call f() again to see the value of $x.
f();
$x: 10 $x: 50

As you can see, the second call of f() outputs 50, thereby confirming that the statement $x = 50 in the closure did indeed have an effect on the outer variable $x, the local variable of f().

The rule is pretty simple: if you want to mutate an outer variable inside a closure, pass it by reference when capturing it in the closure via use.

Normal parameters can be used along with use

While we pass on variables from the enclosing environment to an anonymous using use, it's still completely alright (and rightly so) to pass normal parameters to the function.

Consider the code below:

<?php

function f() {
   static $x = 10;
   echo "\$x: $x\n";

return function($a) use (&$x) { $x += $a; }; } f()(100); f();

The anonymous function returned by f() takes in a parameter $a and adds it to the outer variable $x, mutating that outer variable with the result of this computation.

Also notice the way in which we call this anonymous function here — instead of saving the return value of f(), which is the anonymous function, inside a variable $func and then calling the variable as $func(), we directly call the anonymous function via f()().

Here's the output produced:

$x: 10 $x: 110

The first call of f() echoes '$x: 10' as the static variable gets initialized with 10 with this first invocation of the function f(). Then, the returned function is called with the argument 100. This has the effect of mutating the local variable $x of f() by adding 100 to it.

Finally, calling f() again tells us about the current value of the static variable $x of f(), which turns out to be 110 (100 + 10).