QRdvark.com

Lambdas Anonymous Functions Closures With PHP

Lambdas Anonymous Functions Closures With PHP

  1. Abstract
  2. What are Anonymous Functions?
  3. Passing Functions as Arguements
  4. Closures
  5. Anonymous Functions as Class Properties.
  6. Type Hinting

Abstract

With the advent of PHP 5.3 came Anonymous Functions. Quite often referred to as Lamdas or Closures. In PHP, these are essentially all the same. Any anonyymous function created using the use() keyword is a closure.

What Are Anonymous Functions?

In PHP, an Anonymous Function are functions that are values. What this means is that a function can be assigned to a variable. Not just the result of a function, but the whole function itself. Consider this code:


<?php
        
// assign a function to a variable
        
$lamda = function ($x) {
                
// increment the value of $x
                
return ++$x;
                };
    
// pass a value to the function contained in the variable
        
echo $lamda10 );
?>

The output of the above function will be the number 11. The function, has been assigned to the variable $lamda, and now the variable $lamdba contains the function, and the variable can now be used as a function. This means you can pass arguements to the variable.

Passing Functions as Arguements

As we saw above, an anonymous functions can be assigned to a variable. This variable, can be used as an arguement to another function.


<?php
        
// assign a function to a variable
        
$lamda = function ($x) {
                
// increment the value of $x
                
return ++$x;
                };

        function 
countMessages$num_messages )
        {
                return 
"Your have $num_messages messages";
        }

        echo 
countMessages$lamda(10) );
?>

In the above code, the $lamda variable, containing the anonymous function has been used as an arguement to the countMessages function.

Closures

In the examples so far, anonymous functions have been assigned to a variable, and passed as arguements to a function. This is not really a closure, although, PHP types all anonymous functions as a closure. Confused? Good. Lets resolve this with some code.


<?php
function myClosure$num ) {
    return function( 
$x ) use( $num ){
        return 
$num $x;
        };
    }

$closure myClosure10 );
echo 
$closure)."\n";
echo 
$closure)."\n";

The output of the above code is 20 and 30. The myClosure function is assigned to a variable, which, in essence, is not the function and can be used anywhere throughout the code in the same way as a function.

Now lets get complete closure.


<?php
    
function myClosure() {
        
$local_var 10;
        return function() use (&
$local_var) {
            return ++
$local_var;
            };
    }

    
// create a closure
    
$my_closure myClosure();

    
// call the closure
    
echo $my_closure()."\n";
    echo 
$my_closure()."\n";
    echo 
$my_closure()."\n";

    
// a random closure
    
$random_closure myClosure();
    echo 
$random_closure()."\n";

    
// back to my_closure
    
echo $my_closure();

The output of the above code will be like this:

11
12
13
11
14

What devilry is this?
Lets take a closer look at what has happened here.. A local variable named $local_var has been assigned the value of 10, just as would be done in any function. Within the anonymous function, the use() call is used to bind or close over the variable to create the closure.

The closure retains a reference to $local_var at all times, even after the function returns, which would normally see the variable fall out of lexical scope.

So, when the first three calls are made using the closure, the number increments. Then a new $random_closure is created which does not contain the reference, only $my_closure() contains the initial reference, and so, when this is again called at the end, the number 14 is returned.

So, if another call was added to $random_closure, what would the output be? This is left as an exercise to the reader.

Anonymous Functions as Class Properties


<?php

class myClass{

        
// declare a variable containing an anonymous function
        
public $anon = function($x) { return $x*2; };

        
// constructor, duh!
        
public function multiply$num )
                return 
$this->anon$num );
        }
// end of class

$class = new myClass;
echo 
$class->multiply);

The output of the code will look something like this:

PHP Parse error: syntax error, unexpected 'function' (T_FUNCTION) in /www/a.php on line 6

The simple message is, that this cannot be done.

Type Hinting

Although PHP is loosely typed, type hinting can be used to enforce the type of variable a function recieves. This is true also for anonymous functions, which are of the type Closure.


<?php

/**
*
* Mulitply
*
* @param        int     $num
* @param        Closure $closure
* @return       int
*
*/
function multiply$numClosure $closure )
{
        return 
$closure$num );
}

// a closure
$my_closure = function( $x ){ return $x*2; };

// call the multiply function with a number and a closure
echo multiply10$my_closure );

If a variable that is not of type Closure is passed to the multiply function and error is given.

PHP Catchable fatal error: Argument 2 passed to multiply() must be an instance of Closure, integer given, called in /home/kevin/Desktop/a.php on line 22 and defined in /www/a.php on line 13

A clue is given here on how to handle this error with the world 'Catchable'. At first glance, the word catchable would suggest the error could be caught with a simple try/catch. This would be too easy. So, the PHP Gods have deemed the word catchable, not mean it could be caught in a try/catch.

However, this behaviour can be made to happen with the set_error_handler() function. The below code shows how to gracefully handle this error.


<?php

// error handler function to throw exceptions upon error
function errorToException($errno$errstr$errfile$errline )
{
    throw new 
ErrorException($errstr$errno0$errfile$errline);
}

// set the error handler to the exception
set_error_handler("errorToException");

/**
*
* Mulitply
*
* @param        int     $num
* @param        Closure $closure
* @return       int
*
*/
function multiply$numClosure $closure )
{
        return 
$closure$num );
}

try
{
        
// the second parameter is not a closure and will give an error
        
echo multiply1010 );
}
catch( 
Exception $e )
{
        
// display the error, or handle the exception
        // echo $e->getMessage();
        
echo 'A nice error message';
}