Anonymous functions and closure in PHP

PHP 5.3 introduced function literals to the language, providing big benefits in the form of syntax and readability when using and creating functions that accept other functions as arguments. However, the introduction of anonymous functions was paired with another useful language construct which allows us to close over local variables in the same scope as the anonymous function, creating a closure.

What is closure?

According to Wikipedia, a closure is a

function together with a referencing environment for the nonlocal names (free variables) of that function

A closure is so-called because it is closed over its free variables. These free variables, or upvalues, come from the enclosing scope of the anonymous function and persist even after that scope, and all references to it, is otherwise made unavailable (e.g., by the function execution completing and returning control back to the previous frame).

What does this mean for you, as a PHP developer? If you’re only using vanilla functions to read in and act upon or modify global variables, then probably not much. You can already do something functionally similar (emphasis) to this using the much-maligned global keyword to import variable references from the global scope. However, when working somewhere other than the global scope—or when using anonymous functions—closures are a powerfully expressive means for performing many otherwise tedious and complicated tasks.

What isn’t closure?

First, let’s walk through an example of using the global keyword in a function defined in the global namespace as a basis for comparison against the closures we’ll talk about momentarily.

The global keyword

Suppose you are writing a function that, for some good reason, requires you to change a variable defined outside of the function’s scope. We can import the variable from the global scope into the function scope using the global keyword, like so:

<?php
$count = 0;
function increase_count() {
global $count;
$count++;
}
increase_count();
echo $count;
view rawbasic-global.php hosted with ❤ by GitHub

The output of this script would be 1. The global keyword imports a reference to the global variable $count, whose value at that time is 0, into the function scope. The global variable itself is then incremented. Because we have imported and incremented a reference, this function actually modifies something in the global namespace.

A naive reading of the definition of closure above might lead one to think that this fits the bill. However, the implementation details are slightly different; PHP has a superglobalvariable named $GLOBALS that is accessible from within any scope. The global $var construct is simply syntactic sugar for the much uglier $var = &$GLOBALS['var']; expression, which would have the same effect. Because this is simply a reference to a superglobal, we could in principle observe manipulation of this variable from another source, outside of the function. This is not closure.

Scope within a scope

Another reason this does not create closure is due to the fact that, no matter within which scope you currently operate, global imports only references from the global namespace (due to the implementation reality outlined above). For example, a function within a function cannot import scope from its parent using global, but only from the global namespace.

<?php
$count = 0;
function increase_count() {
$count2 = 1;
function increase_count_again() {
global $count, $count2;
$count++;
$count2++;
}
increase_count_again();
return $count2;
}
echo increase_count();
echo $count;
view rawglobal-inner-function.php hosted with ❤ by GitHub

Running the program above will output 1 1 (no spaces). This shows that global imports variables only from the global namespace, not just from the “parent” namespace. Closure would require either the global statement to grab from the next-highest namespace, or simply allow the parent scope to creep into the child scope (another style of scoping entirely, a la javascript). This is not closure.

Anonymous functions

Is an anonymous function a closure, then? Anonymous functions and closures are often conflated, but they are distinct concepts. I think the garden path I’ve been leading you on so far should have steered you away from this, but let’s just make sure. We know that simply using global isn’t closure because of its implementation-that it doesn’t actually close over variables within the function-defining scope, it just brings them into the current scope by reference. Anonymous functions are still functions, and global operates within them the same way it operates within named functions. Anonymous functions are not closures.

I’m unclear on the low-level implementation details of functions versus anonymous functions in PHP 5.3, but there is no practical difference in terms of the ostensible constraints they face in terms of scope, input, and output. For example:

<?php
$count = 0;
function increase_count() {
global $count;
$count++;
}
increase_count();
$increase_count_again = function() {
global $count;
$count++;
};
$increase_count_again();
echo $count; // should be 2
view rawfunction-literal-global.php hosted with ❤ by GitHub

The only meaningful difference here is that increase_count is given a name at define-time, while $increase_count_again is assigned a function literal which is executed by variable name at run time. They both face the same rules vis-a-vis global, and the fact that this script outputs 2 is proof of that.

The use keyword

So what is closure? You’ve been reading long enough now that you might suspect I’ve eliminated every possible misconception and misunderstanding that could possible arise. That’s not true; closure is a difficult concept to grasp, and misunderstanding it can lead to misuse, which can introduce hard-to-detect bugs.

Closure requires both a function, with its own scope, and a surrounding environment which persists through an arbitrary number of executions of that function. So, an anonymous function is insufficient. In order to produce a closure, we need to be able to create the surrounding environment. Luckily, PHP 5.3 also introduced a very useful construct for turning anonymous functions into closures.

use by value

The use keyword, when paired with a function literal—or anonymous function—allows us to import variable values or references into the scope of the function. Here’s an example:

<?php
$count = 1;
$decrease_count = function() use ( $count ) {
$count;
};
$decrease_count();
echo $count;
view rawsimple-naive-closure-value.php hosted with ❤ by GitHub

We use $count by value, so the output of the script is 1. This is because PHP binds a function-local variable named $count to the value of the parent-local $count at the time the function literal is defined. Even if we change the value of the parent-local $count (say, by incrementing it between defining $decrease_count and calling it), its value within the function body will not change. This also implies that changing $count in the function-local scope will have no effect on the value of $count in the parent-local scope. This is not closure.

use by reference

<?php
$count = 1;
$decrease_count = function() use ( &$count ) {
$count;
};
$decrease_count();
echo $count;
view rawsimple-naive-closure-reference.php hosted with ❤ by GitHub

I keep using the phrase “by value or reference” because either is possible. Notice in the example above that I have prefixed $count with &, the PHP symbol that makes an assignment by reference. The output of the above script is as expected: $count from the parent-local scope is decremented in the function-local scope and the printed result is 0. Notice especially that this particular script need not be defined in the global namespace (the file in which it’s defined could be included in a function, for example); use always refers to the scope in which the function is defined. This is closure.

More basically, you might ask: how is this any different from using global inside a function defined in the global namespace? The most important way is that it allows us to import a value or reference from the immediate-parent scope, not just the global scope. The tuple following the use keyword tells PHP to import values or references to each element into the function scope.

Ok, so you can import variables by reference into anonymous functions. What’s the big deal? Well, now that you understand many of the things that aren’t closure (and have an example of something that technically is closure), let’s see what you can do with it.

Usage of closure

Under usual circumstances and in many languages, any variable allocated within a function body is lost when that function’s execution is completed, because the frame containing it is popped (although this isn’t exactly right in all languages). Within a closure, however, we can maintain and manipulate the information created inside of an enclosing function even after that enclosing function has finished execution. Check out the following example:

<?php
function create_closure( $count ) {
return function() use ( &$count ) {
return $count;
};
}
$closure = create_closure( 10 );
echo $closure(); // 10
echo $closure(); // 9
echo $closure(); // 8
view rawfull-closure-count.php hosted with ❤ by GitHub

In this script, the body of the create_closure function has a local variable named $count. create_closure returns an anonymous function which has by-reference access to this local variable. Therefore, where references to @create_closure@’s $count would typically lose all meaning when its body was finished executing, there now remains a reference to itwithin any variable to which the anonymous function is assigned. create_closure creates a closure because it returns a function together with a referencing environment for the nonlocal names of that function. This is closure.

Notice the output of the above (shown inline as comments). We call the anonymous function three times, and the output changes each time, even though the body of the anonymous function really does nothing interesting. $closure has “closed over” $count and now has local access to it for any call to $closure().