Oneupweb : Closures in PHP—Say Whaaaaat?

That’s right folks. It isn’t a dream, so you can stop pinching yourself. One of the coolest features to hit PHP, as of version 5.3, are closures (a.k.a anonymous functions). Not sure what a closure is? Anyone who has dabbled in
jQuery ought to recognize one:

$(document).ready(function(){
       //run some high powered javascript goodness
});

That function being passed to the infamous “ready” method is an anonymous function. What is so cool about these types of functions, is they can be assigned to variables and passed as arguments to other functions like any other object. Let’s look at PHP’s usual way of passing a function as a callback:

function callfunc($functionName,$args = array()) {
       call_user_func_array($functionName,$args);
}

function mycallback($name) {
      echo "Hello $name !";
}

//to pass it as a callback function to our callfunc function we need to use the name as a string
callfunc('mycallback',array('Brian'));

Not bad…..not good either. We have to create our function and then pass it by name to the function executing our callback. For those of you familiar with WordPress, this is the usual order of things. There has to be a better/much cooler way of doing things!

Enter closures!

Using a closure we can use this much neater, much cooler syntax to pass a callback function:

callfunc(function(){
    echo 'Hello Brian!';
});

Doesn’t that feel good? It doesn’t stop there. You Javascripters out there are probably used to using functions as first class objects all the time:

var myfunc = function() {
    alert('hello');
};
myfunc();

This is doable in PHP 5.3:

$myfunc = function() {
         echo('hello');
};
$myfunc();
//OR
callfunc($myfunc);

Closures and global variables

Just to note, closures operate a little differently with global variables. Take the example of using the WordPress db object, normally you would write something like this:

function mydbfunc() {
    global $wpdb;
    //run super cool database queries
}

Something equivalent in closure town would be:

callfunc(function() use($wpdb){
       //run super cool database queries
});

The key here is the “use” keyword instead of using “global” inside of the function.

Use Case: A simple rest server application using closures!

Some of you may be familiar with Sinatra, a small ruby framework that routes method calls to urls. We can do something very similar with PHP 5.3.
Disclaimer: This is posted as is, and it is a very, I repeat, very simple implementation. I won’t take into account routing all requests to an index file, or setting up views. This is just to show some of the cool possibilities that are available with PHP 5.3 closures. This script assumes you are using a script named test.php (In reality you would want to route all calls to index.php and handle the URI that was being requested…)

$methods = array(
	'GET' => array(),
	'POST' => array()
);

function get($url,$callback) {
	global $methods;
	$methods['GET'][$url] = $callback;
}

function post($url,$callback) {
	global $methods;
	$methods['POST'][$url] = $callback;
}

function listen() {
	global $methods;
	$request = 'http://' . $_SERVER['SERVER_NAME'] . preg_replace('/?.*/','',$_SERVER['REQUEST_URI']);
	$request_method = $_SERVER['REQUEST_METHOD'];
	$callback = $methods[$request_method][$request];
	$global = $_GET;
	if ( $request_method == 'POST' ) {
		$global = $_POST;
	}
	return call_user_func_array($callback,array($global));
}

//start routing some functions to urls
get('http://dev.mysite.com/test.php',function($req) {
	echo 'Hello ' . $req['name'];
});

post('http://dev.mysite.com/test.php',function($req){
	header('Content-Type: application/json');
	echo json_encode(array('key1' => 'mydata', 'key2' => 'myotherdata'));
	exit;
});

listen();
?>
<form action="http://dev.mysite.com/test.php" method="post">
	<input type="hidden" name="postvar1" value="this is a hidden value" />
	<input type="hidden" name="postvar2" value="this is a hidden value too" />
	<input type="text" name="postvar3" />
	<input name="submit" value="submit" type="submit" />
</form>