triptic

Weblog by triptic | Online Communication in Eindhoven

Simulating closures in php versions prior to php 5.3


Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 1904

Warning: Invalid argument supplied for foreach() in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 1904

Warning: Invalid argument supplied for foreach() in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 2257

Warning: Invalid argument supplied for foreach() in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3206

Warning: implode() [function.implode]: Argument must be an array in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3258

Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3289

Warning: Invalid argument supplied for foreach() in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3289

Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3332

Warning: Invalid argument supplied for foreach() in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3332

Warning: array_keys() [function.array-keys]: The first argument should be an array in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3477

Warning: Invalid argument supplied for foreach() in /home/techblog/domains/techblog.triptic.nl/public_html/wp-content/plugins/wp-syntax/geshi/geshi.php on line 3477

Now that PHP 5.3 has closure and lambda function support, we all would like to use it. However, not all of us are able to do so because we are still stuck with hosting providers not able or willing to upgrade to PHP 5.3. Wanting to use closures myself on servers that only support PHP 5.2 I came up with a solution to be able to use closures in PHP 5 versions prior to 5.3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?
class PhpClosure
{
    private $variables;
    private $callback;
 
    public function __construct(array $variables, $callback)
    {
        $this->variables = $variables;
        $this->callback = $callback;
    }
 
    static public function get(array $variables, $callback)
    {
        $closure = new self($variables, $callback);
        return array($closure, 'call');
    }
 
    public function &__get($name)
    {
        return $this->variables[$name];
    }
 
    public function __set($name, $value)
    {
        $this->variables[$name] = $value;
    }
 
    public function call()
    {
        $arguments = func_get_args();
        array_unshift($arguments, $this);
        return call_user_func_array($this->callback, $arguments);
    }
}
?>

Before we take a look at some usage examples, let’s first take a look at some code that uses a closure in PHP 5.3:

1
2
3
4
5
6
7
8
9
10
11
<?
$var = 1;
$f = function($i) use (&$var) {
    $var += $i;
};
echo $var.'<br />';
$f(2);
echo $var.'<br />';
$f(3);
echo $var.'<br />';
?>

This produces the following output:

1
3
6

All following code snippets use the PhpClosure class to do exactly the same thing and produce the same output without PHP 5.3:

1
2
3
4
5
6
7
8
9
10
11
<?
$var = 1;
$f = new PhpClosure(array('var'=>&$var), create_function('$closure,$i', '
    $closure->var += $i;
'));
echo $var.'<br />';
$f->call(2);
echo $var.'<br />';
$f->call(3);
echo $var.'<br />';
?>

This looks a lot like the PHP5.3 code snippet! Of course, if you don’t like the way $closure->var needs to be called you can always add an extra $var =& $closure->var;.

Personally I don’t like the way create_function works. In my opinion the way you need to provide code inside a string makes it just as evil as eval. Also, your editor won’t show syntax highlighting or code completion for code inside a string. To overcome this I’ll give a name to my anonymous function. Now it’s not an anonymous function anymore, but the closure still works:

1
2
3
4
5
6
7
8
9
10
11
12
<?
$var = 1;
function increase($closure, $i) {
    $closure->var += $i;
}
$f = new PhpClosure(array('var'=>&$var), 'increase');
echo $var.'<br />';
$f->call(2);
echo $var.'<br />';
$f->call(3);
echo $var.'<br />';
?>

This is also possible using methods within objects by passing array($this, 'methodName') instead of 'increase'.

The last code snippet shows how to get a variable of the callback type to be able to use it with functions like call_user_func, usort or array_map:

1
2
3
4
5
6
7
8
9
10
11
12
<?
$var = 1;
function increase($closure, $i) {
    $closure->var += $i;
}
$f = PhpClosure::get(array('var'=>&$var), 'increase');
echo $var.'<br />';
call_user_func($f, 2);
echo $var.'<br />';
call_user_func($f, 3);
echo $var.'<br />';
?>

Related articles:

Written by Onno Marsman
Tags: ,

Respond to this post