Making use of PHP closures

October 12th, 2009 | ,

Even though closures in PHP are not quite as easy to use as in other languages, we can still employ them for something useful.

There are couple things we can call in PHP: functions, methods and static methods. Version 5.3 introduced another two callables: closures and objects implementing __invoke() method.

In the previous post we used closures to implement Groovy-like array utilities. Today we’ll see how closures can be used to simplify function calls and how they (don’t?) fit into class-related constructs.

Unified calling convention

Depending on the function type, we use different syntax to invoke it. Let’s start with an example:

function regular_function($a1) 
{ 
	echo __FUNCTION__  . "($a1)\n";
}

class SomeClass
{
	function regular_method($a1)		
	{
		echo __METHOD__  . "($a1)\n";
	}

	static function static_method($a1)	
	{
		echo __METHOD__  . "($a1)\n";
	}
}

$obj = new SomeClass;

To call these functions, we say:

regular_function(1);
$obj->regular_method(2);
MyClass::static_method(3);

This is basic stuff. But what if we don’t want to hard-code the function names? For instance, what if we want to switch static_method with dummy_method for testing purposes? One option is to use call_user_func_array function. But then we still need to check what are we calling (function, method or static method) and invoke call_user_func_array accordingly.

Another solution is to wrap the thing we want to call in a closure. Then we get this:

$c1 = to_closure('regular_function');
$c2 = to_closure('regular_method', $obj);
$c3 = to_closure('static_method', 'MyClass');

// same call syntax for all functions
$c1(1);
$c2(2);
$c3(3);

There is absolutely no difference between calling various types of functions. We can easily pass wrapped functions as parameters (for example, in a configuration file).

How do we implement to_closure function? It’s actually a simple call_user_func_array wrapper:

function to_closure($func_name, $class_or_object = null)
{
	$f = $class_or_object 
		? array($class_or_object, $func_name)
		: $func_name;

	return function() use ($f) {
			return call_user_func_array($f, func_get_args());
		};
}

First we determine if given function is a method or a simple function. Then we just use created callable variable $f in the returned closure.

How is this useful? Think about it this way. Without closures, to achieve the same functionality, you’d probably wrap your function calls in classes. Perhaps three separate classes – for functions, for methods and for static methods. That’s certainly doable, but requires more code. So as usually, it’s all about simplicity (followed by ease of maintainability and modification).

Everywhere we’d use objects to wrap functions (even handling, factories, all kinds of callbacks etc.), we can use closures. How about using closures with classes?

JavaScript style functions? You wish!

This section shows how to use PHP closures in JavaScript way. If you know Ruby, Groovy, Scala or any other language with well implemented closures support, the code below will probably make you smile ;)

Let’s extend our class from the beginning of the article with the following method:

// a class member to be used in closures
var $x = 'this is SomeClass:$x';

public function member_closure_test() {
	// 1) alias $this so it can be accessed in closures
	$self = $this;

	// 2) JavaScript style
	$this->js1 = function() use ($self) { 
		echo "JS simple $self->x\n"; 
	};
	// $this->js1() yields an error
	$f = $this->js1;
	$f();

	// 3) JavaScript style with the array wrapper trick
	$this->js2[0] = function() use ($self) {
		echo "JS with array $self->x\n";
	};
	$this->js2[0](); // For some reason, this works...
}

There are couple things to notice here:

  1. We alias $this reference with local variable $self. That’s because we can’t use $this in use() declaration of a closure.
  2. First attempt to create a JavaScript style function. We need to dereference the closure before calling it.
  3. No temporary variables here. Undeclared arrays to the rescue!

Point 3) is the simplest solution I’ve come up with. As you can see, even with the array wrapper trick, we still need a temporary variable aliasing $this.

In conclusion

PHP wiki site considers using closures for extending classes a “common misconception”.
In other words, the “involved” syntax is not a bug – it’s a feature. Oh well, I don’t want to turn this blog into one big rant, so I’ll leave it at that ;)

On a positive note, closures provide new, more concise way to wrap function calls, which should reduce the number of objects floating around. They can also be used anywhere we’d use create_function before. And that’s definitely a good thing.

Source code for the article includes all presented examples.



Article discussion - 4 comments

Well haven’t tried this out yet, but still if something like this is achievable then it can easily be base to a good php framework. What do you say? I am thinking of update tutorial on “Building a Custom PHP Framework with a custom template caching engine using Output Control functions” http://tinyurl.com/ogy8yh, if this really work out well.

If by “this” you mean wrapping different sorts of callable things into one common construct, then sure, I think it’s very much usable. For example, in you framework, you could replace all those statics with callbacks.

As for using closures in the way we often do it in Javascript, I doubt it’s going to be used at all, for two reasons: the syntax is awful, and we already have __call() which allows for similar tricks (just with a bit more work).

Comment by Pawel on 2009/10/13 at 23:45

Hello from Russia!
Can I quote a post in your blog with the link to you?

Comment by Polprav on 2009/10/17 at 15:22

sure :)

Comment by Pawel on 2009/10/17 at 22:35

Submit your comment