In the past few years, there has been a lot of activity around programming languages. Developers have been studying languages like Ruby, Groovy, and Clojure — not just to broaden their marketability but also to expand their thinking. Two of the more exciting features of these languages are now available with PHP 5.3: lambdas and closures. And there's another language that most PHP developers already use that has an excellent implementation of lambdas and closures: JavaScript.
With the addition of lambdas and closures, PHP complies with the requirements of a functional programming language. Specifically, functional programming languages must provide high-order and first-class functions, which this article defines. In the past couple of years, interest in functional programming has skyrocketed. Why? Simple: Moore's law, which states that computing power will double every two years, is really no longer true. It is only somewhat not true because whereas computing power had been increased on a single-core processor, computer manufacturers can now only increase capacity by creating multi-core processors. That's why we've been seeing dual-, quad-, and hexa-core processors. Making multi-core processors is relatively easy for computer manufactures, but stateful, stacked-based languages don't necessarily benefit from additional processors. Functional programming languages can.
Just what are lambdas and closures and why should a PHP developer get excited about their availability? This article describes how and where to use lambdas and closures, exposes you to some of the behavioral peculiarities of closures, and introduces you to a few code idioms you can use to take advantage of lambdas and closures.
The term lambda is from Lambda calculus, a formal system for function definition, application, and recursion introduced by Alonzo Church in the 1930s. One interesting thing about Mr. Church is that his co-workers at Stanford University included Alan Turing of the famous Turing machine. The ideas that these two men formulated became the basis for modern programming languages.
But don't let the complexity of calculus dissuade you from using lambdas: They are really nothing more than anonymous functions. The following function definition, for example, is a lambda:
$horse = function() {return "with no name";};
print $horse();
|
Note that the semicolon (;) terminator is required, as
the function is being assigned to a variable. Prior to PHP 5.3, you would
have created a standard function:
function horse() {return "Secretariat";}
print horse();
|
"Big deal!" you say. "Both examples have the same number of lines, and besides — the nameless function basically did have a name in the $horse
variable." Actually, $horse is a reference to a function,
not the name of the function. But the real advantage to lambdas is when you create
functions on the fly and no variable reference is required. For example:
$a=array('PA'=>"Pennsylvania",'VA'=>"Virginia",'TX'=>"Texas");
print_r(array_filter($a,
function($v){return $v==="Virginia";}
)
);
|
In this case, the lambda function remains nameless: It is created inline as a
parameter to the array_filter function. Creating lambdas
on the fly as parameters to functions and methods is lambda's primary use case.
Note that the term callback is typically used to describe functions that
are passed as parameters to other functions. Callbacks are frequently used in event
handling architectures, where you designate code to be invoked at a later time.
In functional programming, lambdas are referred to as first-class functions because they can be:
- Passed as a parameters.
- Created and used at run time.
- Returned from a function.
- Assigned to variables.
In truth, lambdas have been available to PHP developers since V4.0 with the
use of create_function:
$horse = create_function('', 'return "with no name";');
print $horse();
|
But the syntax for create_function is clunky. For one
thing, the code is in one big quoted string. So don't expect any assistance from
your PHP editor, and don't forget to escape that code string. Furthermore, the
code for create_function is compiled at execution
time, and the resulting function cannot be cached.
Here's a simple example that uses a lambda — one that I will use throughout
the rest of this article. Here's the application requirement: When I read PHP
articles that contain code snippets, they typically show results with an
echo or print followed
by a comment that describes the expected output. Consider, for example, the
following code:
function horse() {return "Secretariat";}
print horse(); // should list: Secretariat
|
The reader needs to run the code and visually verify the implied assumption. When I read:
print horse(); // should list: Secretariat |
I feel like I'm going cross-eyed as I oscillate between the code and the comment.
To make this article's code snippets more readable, I use PHP
asserts. For example:
assert(horse() == "Secretariat"); |
The configuration of PHP assertions allows you to specify a function to be called
whenever assert is invoked. Of course, I used a
lambda:
assert_options(ASSERT_CALLBACK,
function ($script, $line) {
echo "assert failed file:$script line:$line ";
}
);
|
When an assertion fails, the script name and line number will be output:
assert failed file:/var/www/closure_behavior.php line:13 PHP Warning: assert(): Assertion failed in /var/www/closure_behavior.php on line 13 |
An alternative to the use of a lambda in the assertion would have been:
function assertCallback($script, $line) {
echo "assert failed file:$script line:$line ";}
);
assert_options(ASSERT_CALLBACK, assertCallback);
|
"And why is the lambda version better?" you might ask? Let me explain. Keep in mind that the non-lambda version defines a function that is only used in one place. Understand that code is read far more than it is written, so simplifying code is important. Whenever you define a named function, your reader attempts to put that function name in his or her brain cache, which needlessly complicates the reader's understanding of the program. If a function is to be a once-and-done operation, use a lambda and simply your code.
Throughout the rest of this article, whenever you see an assert,
you can be sure that the assertion is true.
If lambdas are nothing more than nameless functions, then closures are little more
than lambdas with a context. That is, a closure is an anonymous function
that, when created, attaches to itself the value of variables from the scope of the
code that created the function. Variables are bound to a closure with the
use keyword. For example, if you have the following
variables:
$states=array('PA'=>"Pennsylvania",'VA'=>"Virginia",'TX'=>"Texas");
$st = 'Texas';
|
You can bind the $st variable to the closure with
the use keyword in the following example:
$tx = array_filter($states, function($v) use($st) {return $v==$st;});
|
Then assert success:
assert(array_keys($tx) == array('TX'));
|
As the above example illustrates, a closure is a function that is "closed over" specified
variables. The above example is a good use case for closures: It uses one of the
18 standard PHP array functions that take a function as a parameter (such as
array_walk, usort, and
array_app). Closures allow you to expand the use of
those utility functions. Before closures, implementation of the passed function
was limited to the context of its function arguments. (Actually, that is not completely
true, as you could have used globals. But it is conventional wisdom that the use
of globals is a bad programming practice.)
PHP's array_filter is an example of what is know in functional
programming as a higher-order function. A higher-order function
(in both mathematics and computer science) is one that takes a function as
input or outputs a function. After becoming comfortable with lambdas and closures,
you may find yourself reducing code and creating more readable and reusable code
by writing your own higher-order functions.
A closure is actually a new data type implemented internally with the new
Closure class. Here's another way to look at it: If an
object is data with methods that operate on that data, then a
closure is a function with data bound to that function.
It is easy to understand the syntax of a closure's use keyword.
But it takes a bit of playing around with closures before you begin to understand
the behavior of variables passed to closures via the use
keyword. I'm sure you have a solid understanding of variable scope: Well-written
code defines variables only in the scope for which it is required. With that in mind,
you would expect the assertion at the end of the following code to fail:
function getNameFunc() {
$string = 'Denoncourt';
return function() use ($string) { return $string; };
}
$name = getNameFunc();
$name();
assert ($name() == 'Denoncourt');
|
But the assertion is successful — even though $string
went out of scope. That's because variables specified in a closure's use
keyword are lexically scoped. Let me explain: PHP variables are typically dynamically
scoped, which means the variables are placed on a stack. Those variables pop off
the stack when their containing function is complete. Lexically scoped
variables refer to the local environment in which they were defined. Instead of being
placed on a stack, the variables specified with the use
keyword are stored in static storage.
It is a powerful feature of closures that — regardless of where they are
defined—they can be called later, from anywhere, and executed with their
use-specified variables still in a valid scope. Be aware, however, that PHP's
implementation of lexical scope copies variables specified in the use
keyword into the static storage area. When using large objects, you can reduce
storage by passing a reference to the variable, like so:
$string = 'Denoncourt';
$changeName = function() use(&$string) {$string = 'Smith';};
$changeName();
assert ($string == 'Smith');
|
As the above example also shows, passing use variables by reference is one way you can allow the closure to modify values.
"Wait a minute," you say. "Why not just pass the variable that you passed in the closure's
use option as a function argument?" That's where the
power of lexically scoped variables comes into play. With function arguments, the
parameters passed must be in stack scope. But, with use variables, the closure
essentially remembers the context of the variable. Consider the example in Listing 1, where a closure is defined in a class, and then invoked after the object variable goes out of scope.
Listing 1. Invoking a closure after the variable goes out of scope
class Person {
var $first;
var $last;
public function sayHello() {
$that = $this;
return function() use ($that) {
return $that->first.' '.$that->last;
};
}
}
function stackFunc1() {
$me = new Person();
$me->first = 'Don';
$me->last = 'Denoncourt';
return $me->sayHello();
}
$sayHi1 = stackFunc1();
function stackFunc2() {
$me = new Person();
$me->first = 'Sue';
$me->last = 'Swartzbaugh';
return $me->sayHello();
}
$sayHi2 = stackFunc2();
assert($sayHi1() == 'Don Denoncourt');
assert($sayHi2() == 'Sue Swartzbaugh');
|
Note that the Person::sayHello method implementation
creates an alias for the $this special variable and
passes that to the closure definition. That is because a closure is internally
implemented with the Closure class, and that class has
its own $this context. In fact, if you pass
$this in the use keyword,
you will get an error similar to this:
PHP Fatal error: Cannot use $this as lexical variable |
The use cases for class methods that return functions is limited only by your creativity. For an example of the creative use of closures, check out Pawel Turlejski's utility class, which provides Groovy-like array methods (see Resources).
With the use of closures, you can even code your classes to allow the dynamic creation of methods. Listing 2 provides an example.
Listing 2. Coding with dynamic method creation
class Person {
var $first;
public function __call($method, $args) {
return call_user_func_array( $this->$method, $args);
}
}
$me = new Person();
$me->first = 'Don';
$me->sayGoodbye = function() use ($me) {
return 'Bye '.$me->first;
};
assert($me->sayGoodbye() == 'Bye Don');
|
Again, even if the object goes out of scope, the closure can be successfully invoked.
For example, the above closure execution works even if you removed the
$me object reference with PHP's unset
method:
$bye = $me->sayGoodbye; unset($me); assert($bye() == 'Bye Don'); |
Arguments passed to higher-order functions, and then passed on as arguments to a
closure's use keyword also retain their values:
$greeting = function($greet) {
return function($name) use($greet) {
return $greet.' '.$name;
};
};
$hi=$greeting('Yoh');
assert($hi('Don') == "Yoh Don");
assert($hi('Sue') == "Yoh Sue");
|
Earlier, I mentioned that closures were implemented internally with a class called
Closure. (That class, by the way, may change in subsequent
versions of PHP, so don't use it directly in development.) But there is one feature of
the implementation internals of the Closure class that
you can exploit: its use of the new __invoke method.
The __invoke magic method makes a class callable:
class Invoker {
public function __invoke() {return __METHOD__;}
}
$obj = new Invoker;
assert ($obj() == 'Invoker::__invoke');
|
When a class that contains an implementation of __invoke
is instantiated, you can call the class by specifying parentheses (())
following an object name. This type of object is known as a functor
or function object.
Your applications can benefit from the use of lambdas and closures. By replacing formal function definitions with anonymous functions, you simplify code. By developing utility functions and methods as higher-order functions, you make your applications more robust. By understanding closures and lexical scope, you begin to perceive opportunities for new development patterns and idioms. And by learning more about functional programming, you broaden yourself as a developer.
Should PHP be used as a functional programming language? In general, no: PHP evolved as a lean, simple language for the development of web applications. Developing PHP-based Web applications using purely functional semantics goes against the original intent of the language. In any case, PHP's syntax for lambdas and closures is somewhat inelegant (especially when compared with languages like Ruby, Groovy, and Clojure). But there are scenarios where the application of functional programming concepts can be applied to PHP applications, resulting in code that is simpler, more concise, and easier to understand.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample PHP scripts for this article | os-php-lambda-Closure_example.zip | 4KB | HTTP |
Information about download methods
Learn
-
Check out "Making use of PHP
Closures" and discover closure usage and caveats from Just a Few Lines.
-
Visit RFC: Lambda functions and closures for comments on lambdas and closures.
-
Read "What's wrong
with PHP closures," which explains a class that provides Groovy-like
array methods.
-
Read "Real
programming with PHP 5.3: JavaScript-style classes" to discover another creative
use of closures.
-
See "An
intriguing use of lambda functions" for another creative use of lambdas.
-
Check out "Remove
empty array elements with recursive lambda in PHP 5.3" for yet more creative uses
for lambdas.
-
In Clojure — Up-Close and Personal, Bob
Martin introduces Clojure in his video podcast.
-
PHP.net is the central resource for PHP developers.
-
Check out the "Recommended PHP reading list."
-
Browse all the PHP content on developerWorks.
-
Follow developerWorks on Twitter.
-
Expand your PHP skills by checking out IBM developerWorks' PHP project resources.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Using a database with PHP? Check out the Zend Core for
IBM, a seamless, out-of-the-box, easy-to-install PHP development and production environment that supports IBM DB2 V9.
-
The My developerWorks community is an example of a successful general community that covers a wide variety of topics.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products, as well as our most popular articles and tutorials.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
- Download
IBM product evaluation versions
or explore
the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.
-
Participate in the developerWorks PHP Forum: Developing PHP applications with IBM Information Management products (DB2, IDS).
Don Denoncourt is a free-lance consultant, trainer, mentor, and author specializing in Java technology, Groovy, Grails, and PHP. You can reach him at dondenoncourt@gmail.com.




