Closures coming in PHP 5.3 and that’s a Good Thing

Tags:

PHP 5.3 will be introducing closures to PHP. Closures, also known as anonymous functions, will allow you to declare functions “inline” and store them in variables. While the syntax may seem a bit weird compared to how it is in languages like JavaScript, closures will be a useful addition to the language. However, like reflection, it’s a feature that may not immediately show it’s usefulness…

Simple example

Let’s take a quick example of how you would define a closure:

$closure = function($param) { echo $param; };
 
//This one takes value of someVar and "stores" it in the closure's scope even if
//we later change the value of someVar outside it. We assume that $somerVar is defined before this
$closure2 = function($param) use ($someVar) { echo $param . ' ' . $someVar; };

Note that in earlier PHP versions, you can achieve a similar effect by using create_function. There are some problems with create_function, though, and using it is generally not recommended. Closures in 5.3 will work without the issues of create_function – not to mention have a much better syntax as you won’t have to escape your stuff in a string!

Next, let’s take a look at some ways you can immediately benefit from closures.

Use case: outputting HTML

In bigger applications it’s often typical to have more than one or two similar views of data. For example, you may want to have a paginated list of some kind of data stored in the database.

To preserve similar styling across your application, you should put code which outputs common elements such as these lists inside functions or classes so they are easy to reuse. However, there’s one problem here: what if you have some data, which requires a bit different formatting for the rows in the list? Maybe it needs an icon, or maybe you want to add some JavaScript code to run when you hover over the item.

You could add some code to the function, which allows the user to pass some parameters to it which change the way the rows are outputted, but this can lead to much clutter in the function itself, and when you need yet another slightly different list, you again need to modify the function.

Or, you could use a closure to provide a so-called “formatter” function to it!

function item_list(array $items, $formatter = null) {
  //create the default formatter
  if($formatter == null) {
    $formatter = function($row) {
      return '<p>' . $row . '</p>';
    };
  }
 
  $html = '<h2>Listing:</h2>';
  foreach($items as $item) {
    $html .= $formatter($item);
  }
 
  return $html;
}

So there we have an example list-creator function. It can take two arguments: the array of data to output, and an optional formatter.

//show a list of numbers using the default format:
echo item_list(array(1,2,3));
 
//let's show it in bold instead:
echo item_list(array(1,2,3), function($row) {
  return '<p><strong>' . $row . '</strong></p>';
});

Other uses

Closures could also be used as event handlers. If you’ve used JavaScript, chances are you’ve used something like window.onload = function() { … }.

Another good use is with functions such as array_map or usort: They take a function as an argument, which is then called once for each item in the array passed as the other argument. These functions aren’t always very complex, or maybe they aren’t used in more than one place, so you wouldn’t necessarily need to have them as separate functions.

For example, a recent question in #zftalk was about calling a specific method in each object in an array, and putting the result to another array:

$results = array();
array_map(function($row) use (&$results) { $results[] = $row->someMethod(); }, $someArray);

In this example, the use() is needed for the closure to be able to access $results. We also need to pass results as a reference, since the modifications we do inside the closure won’t otherwise affect it. What this code does could be achieved with a loop too.

Maybe a more typical example of a one-use function is with usort, which sorts an array by using a user defined sorting function:

$arr = array(2,1,3);
usort($arr, function($a, $b) { return $a < $b ? 1 : -1; });

Difference between use() and globals

You may wonder why do you need use() when you could use globals. It’s true that in a simple case using global $foo or use($foo) are pretty much the same, but there is a difference.

Imagine a case where you write a function, and there’s a local variable inside the function. You want to use this variable inside the closure.

In this case, we need to use the use clause. If we used global, it would attempt to look up the variable from the global scope. Even if there is no such variable available globally, it won’t use the local variable in the function. With use(), the local variable can be used inside the closure.

In closing

As you can see from the example, closures are quite useful if you just know how to use them. We even could modify the listing function to output some details from a class – we can pass it an array of class instances, and a function which calls a specific method of the class instead of just showing the $row variable itself. Too bad it’s probably going to take a while before PHP 5.3 is common enough to rely on this feature much.