What’s wrong with PHP closures?

October 2nd, 2009 | ,

PHP 5.3, along with many other features, introduced closures. So now we can finally do all the cool stuff that Ruby / Groovy / Scala / any_modern_language guys can do, right? Well, we can, but we probably won’t… Here’s why.

Many languages provide nice and concise way of dealing with collections. This article presents “objectified” arrays implementation to demonstrate usage of closures in PHP 5.3.

Before we begin, please keep in mind that the code used is adjusted for educational purposes – performance aspects are not covered at all.

How others do it

Let’s start with a simple example. For the following array:

$nums = array(10, 20, 30, 40)

The task is to find all elements greater than 15.

So before without closures, we’d probably write something like this:

$res = array();
foreach ($nums as $n)
	if ($n > 15)
		$res[] = $n;

In languages with closures support, this could look more like this (Groovy):

def res = nums.findAll { it > 15 }

or like this (Scala)

val res = nums filter (_ > 15)<

Notice how in Groovy and Scala we get pretty one-liners, because the looping part is abstracted away. With closures, we can do similar thing in PHP:

$res = array_filter($nums, function($v) { return $v > 15; });

We got almost twice as many characters with PHP as with Scala, but it’s still quite readable.

By the way, the PHP code above uses a lambda, not a closure, but the difference is not important right now. This site provides details on PHP closures and lambdas.

So far so good. Let’s consider another task: find all elements greater than 15 and multiply them by 2, and then add local variable x to each element.

Groovy:

def x = 1
def res = nums .findAll { it > 15 } .collect { it * 2 + x }

Scala:

val x = 1
val res = nums filter (_ > 15) map (_ * 2 + x)

and PHP:

$x = 1;
$res =
array_map(
	function($v) use ($x) { return $v * 2 + $x; },
	array_filter(
		$nums,
		function($v) { return $v > 15; })
);

Now that’s not even close to Groovy or Scala.

Aesthetics aside, there’s an additional problem with the code above. What if we wanted to filter array elements based on keys, and not values? Well, we can’t. At least not without some workarounds.

Imperative way of achieving the same thing would be as follows:

$x = 1;
$res = array();
foreach ($nums as $n)
	if ($n > 15)
		$res[] = $n * 2 + $x;

Perhaps it’s a bit cleaner, but when you see a code like this, you have to stop for a moment, before you actually see it: “ohh it’s just an array transformation”. So, let’s see how we can employ some rarely used PHP features to achieve something similar to what other languages have to offer.

ArrayObject – the array() wrapper

PHP comes with a standard library called SPL. One of the classes it provides is ArrayObject, which basically “allows objects to work as arrays”. For example

$res = new ArrayObject(array(10, 20, 30, 40));
foreach ($res as $v)
	echo "$v
";

Since ArrayObject is a built-in class, it can be extended like any other user-defined class.

Arr – wrapping the wrapper

Having ArrayObject class and closures at our disposal, we can start implementing our wrapper:

class Arr extends ArrayObject
{
	static function make($array)
	{
		return new self($array);
	}

	function map($func)
	{
		$res = new self();
		foreach ($this as $k => $v)
			$res[$k] = $func($k, $v);
		return $res;
	}

	function filter($func)
	{
		$res = new self();
		foreach ($this as $k => $v)
			if ($func($k, $v))
				$res[$k] = $v;
		return $res;
	}
}

Now the PHP solution for the second task discussed earlier could be rewritten as follows:

$res = Arr::make($nums)
	->filter(function($k, $v) { return $v > 15; })
	->map(function($k, $v) { return $v * 2; });

What’s the main difference here? First of all, the methods are chainable, so we don’t lose readability as we ad more filters or more complicated mapping. Also notice that the closures passed to filter and map take two parameters – key $k and value $v. This allows us to use key value in a closure, which is not possible with PHP standard array_filter function.

As a side effect we also get more consistent API. With native PHP functions, we sometimes pass the closure as a first argument, sometimes we pass the array first. Sometimes we even pass more than one array…

Source code for this article contains more complete version of the Arr class. Other useful functions (like reduce or walk) are implemented in a similar fashion.

Is it worth it?

I think there is no clear answer for this question – it all depends on the context and programmer’s preferences. When I first saw the syntax for PHP closures, I got a flashback from the old Java days, when I thought that anonymous inner classes could be used as closures. Well, they could, but it’s like putting a lipstick on a pig. PHP closures are not as bad, but the syntax is just plain ugly.

Other languages with closures support provide really nice syntax to use them. You can use regular loops in Scala as well, but why would you? Someone could say that you can use closures this way in PHP too, but why would you?

I’m sure closures will find their uses in the PHP world (like delayed execution or automated resource management), but IMHO replacing traditional loops and array operations is not one of them. Unless, at some point, they decide to break backwards compatibility and clean-up the syntax and API…

In conclusion

Like with most other language features added as an afterthought (remember Java generics? or not to look that far – PHP OOP?), it will take a while before closures gain wider attention. Meanwhile, since more and more web hosting providers introduce PHP 5.3 support and PHP 6 is on the way, it’s definitely a good idea to try out this new tool.

To answer the question from the beginning. If you compare this

$res = Arr::make($nums)
	->filter(function($k, $v) { return $v > 15; })
	->map(function($k, $v) { return $v * 2; });

to this

val res = nums filter (_ > 15) map (_ * 2)

the answer becomes clear: it’s the syntax, that’s what’s wrong.

I encourage you to download the source code for this article, where you can find more examples of how the functional approach to looping looks like in PHP.



Article discussion - 25 comments

For now I prefer to stay with the pure object-oriented approach. I see later when some functional programming can be applied successfully to php.

The syntax is really verbose. Plus, to make it perform really well, we’d need some better collections library. Anyway, these kinds of things are now possible in PHP, at least on paper ;)

Comment by Pawel on 2009/10/03 at 13:36

Never saw the need for closures, et al in PHP. All the examples and those possibilities that I’ve seen on the web can be abstracted away in a class.

There you have a nice, clean and safe structure, easily maintainable and testable so just more feature creep in my opinion and as such we can do without it.

Good post by the way ;)

Comment by Les on 2009/10/06 at 21:33

@Les: Thanks :)

For a person coming from C/C++/Java world (like myself), even __call() is an abomination at first :)

We can do without closures and other stuff coming in 5.3+, but I think the new possibilities are at least worth exploring. Also closures are so trendy recently ;)

Seriously though, of course good design is always important. But I also believe that Java-kind of OOP is aging fast and sooner or later, we’ll have to deal with new solutions to old problems. So, IMHO it’s good to be prepared to use them :)

Comment by Pawel on 2009/10/06 at 22:52

I’m not sure what your point it… you confuse anonymous functions and closures, and your complaint seem to focus more on the verbosity of PHP’s native functions.

A closure binds variables outside a lexical scope to variables inside. It is useful with anonymous functions, but it is not limited to them; closures can be used with with named functions as well.

PHP has tended to lean towards verbosity for the sake of clarity. For example, array( ) instead of a [ ] shorthand notation. http://wiki.php.net/rfc/shortsyntaxforarrays Or using backslash as a distinct namespace separator. http://wiki.php.net/rfc/namespaceseparator Or calling the scope resolution operator Paamayim Nekudotayim. http://php.net/manual/en/language.oop5.paamayim-nekudotayim.php

The *real* problem with PHP’s closures is that the team chose to use the keyword “use”, which is already used to import and alias namespaces, and violates the “distinctness for clarity” rule.

Comment by Timothy on 2009/10/07 at 02:08

@Timothy: I didn’t want to go into too much detail.

There is a paragraph in the post where I explained that and linked this site as a reference: http://wiki.php.net/rfc/closures

But you’re right, verbosity is my main complaint. C# guys somehow managed to introduce lambdas with decent syntax. PHP guys didn’t, hence my rant ;)

Comment by Pawel on 2009/10/07 at 02:53

I would suggest to move the static method out into a global function, to ease the syntax ever so slightly:

$res = arr($nums)
->filter(function($k, $v) { return $v > 15; })
->map(function($k, $v) { return $v * 2; });

Comment by troelskn on 2009/10/07 at 15:06

@troelskn: Good idea, this would be like poor man’s companion objects from Scala :)

@Lucas: I read your post, and well, can’t add much. Let’s just hope that JVM/.NET platforms gain better momentum in the “non-enterprise” market, so we finally don’t have to decide between PHP with 1000′s hosting options or Java/JVM with 10′s…

Comment by Pawel on 2009/10/07 at 18:08

Lucus,

The fall of PHP? Yes… People have been talking about the fall and demise of PHP for the last 5 years or so but hey – the platform is still going strong and it can’t be that bad if Microsoft has [recently] invested time and dollars in it.

Let me know when you [finally] wake up and see that PHP is a heavy weight in the development world – until then all you are doing is making me laugh (loudly).

Comment by Les on 2009/10/10 at 19:44

Congrats pal!

This is the first blog on PHP that does not suck…

Comment by Foo Bar on 2009/11/02 at 09:09

Thanks :)

I wish I had more time to post…

Comment by Pawel on 2009/11/02 at 18:04

Sorry for the late comment.

Your correct in your observations of PHP, but you missed the reason my these features were added. (http://wiki.php.net/rfc/closures)

It was intended to clean up some issues with current use of things like create_function (http://us2.php.net/create_function).

PHP has always resisted allow you to have two ways of doing the same action. This is part of PHP’s intentions of an easy language to learn.

So while your correct, I feel you’ve left out the reasons why it was done that way.

Hey Sam,

Yeah I read that Wiki. The problem is, they added closures, but it seems like it’s only because it’s trendy and cool these days (groovy, ruby, scala, everyone has them). As a result, the feature is there, but it’s cumbersome to use.

> PHP has always resisted allow you to have
> two ways of doing the same action.

PHP is built incrementally, features are just tossed in randomly (at least it seems so sometimes), so redundant functionality is very common. Just take a look at PHP function list…

Personally, I think they did closures this way because it was easier. Otherwise, they’d have to improve PHP parser, and that’s why we got this clumsy syntax.

At least we can forget about create_function, which is obviously a good thing :)

Comment by Pawel on 2009/11/03 at 18:29

Why is everyone obsessed with these one-liners? If reducing the amount of typing is the main goal, then I would just use Ruby On Rails. PHP is for speed, not to be pretty.

Comment by Some Guy on 2010/10/08 at 02:27

I wouldn’t call it obsession, but the less code you have to look at, debug and work with, the better :)

Comment by Pawel on 2010/10/08 at 10:24

Your use of closures is very similar to what one would do in Haskell or Scheme: use an anonymous function (lambda) as an argument to some higher order function.

Comment by Jason on 2011/03/29 at 23:38

Amazing, I searched a way of doing that kind of stuff in PHP !
Thank you very much for helping me with this old language !

Do you think we could create arrays declaratively like :
[2, 3] and change the -> in . ?
and remove this crappy useless $ ?
so we could do : [2, 3].map(function(x) {return x + 2;});

Comment by Thibaut Assus on 2011/08/31 at 10:47

This kind of syntax would be nice, but… it’s still the good old PHP :)

Comment by Pawel on 2011/09/04 at 17:07

Submit your comment