Anonymous Functions in PHP

Anonymous functions have enjoyed a long history with PHP. Well, some version of anonymous functions. PHP 4, released in the long-ago of 2000, had several functions that required the passing in of callbacks, including usort, array_map, and array_walk. To use these you could pass in a callable by name or reference to a lambda (anonymous function) created by create_function. Then in 2009 came a huge step forward with the Closure class, which allowed a more dynamic way to create both lambdas and closures (anonymous functions with flair).

But what does this matter? Why would a developer want to use anonymous functions? Well, they do make using callback-requiring functions easier. And they encapsulate one-off logic in a nice way. One of the nicer uses I've run into is the ability to pass them around as a way to inject logic or, in some cases, help build out ad-hoc classes. In the end they make for a lightweight way to encapsulate and share functionality without creating (too much) spaghetti code.

To better illustrate how all this works, let's say that you wanted to loop through a list of strings and do some modification to them. The list of strings are titles of something and you want to make url-friendly slugs for SEO purposes. Previous to PHP 5.3 there were two ways to do this using callables.

  1. $titles = array(

  2. 'Cold Morning Hikes in the Keweenaw',

  3. 'Long Hikes at Huron and Silver River',

  4. 'Cedar-Cliff Loop of the Huron Mountains',

  5. );

  6. // option 1 - globally available function

  7. function urlFriendlySlug($string) {

  8. $string = strtolower($string);

  9. $string = str_replace(' ', '-', $string);

  10. return $string;

  11. }

  12. $slugs = array_map('urlFriendlySlug', $titles);

  13. // option 2 - limited lambda

  14. $slugs = array_map(create_function('$string',

  15. '$string = strtolower($string); ' .

  16. '$string = str_replace(" ", "-", $string); ' .

  17. 'return $string; '), $titles);

The difference is fairly stark. By using option 1, defining a globally available function, you end up polluting the global namespace. Even if you use the option to pass in a method of a class you are still creating a function just to use it as a callback. Which may be okay. If 'urlFriendlySlug' is used throughout the application, this may be desired. If you're interested in the benefits of a one-off function, the second option is much better (if messy).

PHP 5.3 brought with it the idea of a Closure class, which really moved anonymous functions forward. A new magic method, __invoke, was added to PHP, and anonymous functions were instances of Closure that used __invoke. (__invoke gets called when you call an object as if it was a function, an awesome shorthand for simple utility-like classes). Most of that is behind the scenes. Creating an anonymous function in PHP 5.3 is semantically pretty awesome.

  1. // option 3 - Closure lambda

  2. $slugs = array_map(function ($string) {

  3. $string = strtolower($string);

  4. $string = str_replace(' ', '-', $string);

  5. return $string;

  6. }, $titles);

You can also store anonymous functions in variables and pass them around. You could do this with either create_function or by using Closure's lambdas, although the latter just looks prettier.

  1. // aside - saving a function as a variable

  2. $urlFriendlySlug = function ($string) {

  3. $string = strtolower($string);

  4. $string = str_replace(' ', '-', $string);

  5. return $string;

  6. };

  7. // just do one title

  8. $slug = $urlFriendlySlug($titles[0]);

  9. // run through all the titles

  10. $slugs = array_map($urlFriendlySlug, $titles);

The last type of anonymous function is an expanded form of Closure lambadas… closures. Which is a lambda that allows easy injection of sticky context. If you have some parameters that need to stay constant during the lifecycle of an anonymous funciton you can bind them. This sounds more weird than it is.

  1. // option 4 - Closure closure

  2. $trim_slugs = true;

  3. $urlFriendlySlug = function ($string) use ($trim_slugs) {

  4. $string = strtolower($string);

  5. if ($trim_slugs) {

  6. $string = trim($string);

  7. }

  8. $string = str_replace(' ', '-', $string);

  9. return $string;

  10. };

  11. $slugs = array_map($urlFriendlySlug, $titles);

The key with this example is that array_map is passing in each value in the list of $titles as a dynamic argument of the main function(). $trim_slugs is always 'true' through each call, because that's the value that was bound when the closure was defined. This can be really powerful when you define a closure in a certain scope and want to carry a piece of that scope out with the callback.