Backend Development

Learn About Anonymous Functions and Closure Class In PHP

Learn About Anonymous Functions and Closure Class In PHP

In this article we will learn about Anonymous Functions and closures in PHP. And we will look at the PHP Closure class and the first callable syntax in PHP 8.1.

 

 

 

Anonymous functions or Closures is an important topic in many programming languages. If you are coming from Javascript world, you already recognize the term “Closure”. In PHP there is also support of Closures through the use of the Closure class. 

A Closure or anonymous function in it’s simplest definition is a function without a name like so:

function() {
};

Closures and Anonymous functions plays an an important part in functional programming and can be defined in various ways. The most typical method for declaring anonymous function is to assign it to a variable:

<?php

$sum = function($num1, $num2) {
    return $num1 + $num2; 
};

Note here i am using the function keyword and we are not provided a name then followed by parentheses and semicolon and the end. To invoke this function simply by using the variable name followed by parentheses:

echo $sum(10, 20);

If you inspect the type of the $sum variable you will see that it’s an instance of PHP Closure class:

var_dump($sum);

//
object(Closure)#1 (1) {
  ["parameter"]=>
   ....
   ...
}

You can also declare anonymous functions using PHP 7.4 arrow functions syntax fn()=>:

$sum = fn($num1, $num2) => $num1 + $num2;

The arrow function syntax is shorter than normal function declaration, however note that arrow functions is limited if the function body is one statement only. 

 

Parent Scope Variables

 What about if we want to access variables from outer scope in the Closure function:

$printMessage = function() {
    echo $message;
};

$printMessage();

If you run this code you will encounter this error:

Warning: Undefined variable $message in ...

To access outer scope variables in Closures declared this way, by using the use keyword like this:

$printMessage = function() use ($message) {
    echo $message;
};

$printMessage();

// Hello World

The use keyword allows to access parent scope variables from inside the anonymous function. 

However if you declare the Closure using arrow function syntax the use keyword is not needed as arrow functions have access to outer scope variables automatically. So the above code can be rewritten as:

$printMessage = fn() => $message;

echo $printMessage();   // Hello World

 

Importance of Closures

Why we need to define functions using anonymous function syntax instead of normal functions. Anonymous functions or Closures have many uses, among of these uses is passing the anonymous as parameters to other functions:

function calculate($num1, $num2, callable $func) {
   return $func($num1, $num2);
}

In this code the calculate() function accepts $num1, $num2 and a callable $func. The callable type in PHP denotes a callback. In the function body i am invoking the callable $func . Now when calling calculate() we need to provide the Closure like so:

echo calculate(4, 6, function($x, $y) {
    return $x + $y;
 });

The advantage of this approach is that you can create generic functions that execute multiple things regardless of the inner implementation of the Closure.

For example in the calculate function above it adds two numbers but we can invoke it with different callable to make a different calculation:

echo calculate(4, 6, function($x, $y) {
    return $x * $y;
 });

In this code the calculate() function multiply two numbers.

You can detect if the supplied argument is a callable using PHP is_callable() function. This function return true if the argument is a function and can be called from the current scope:

function calculate($num1, $num2, $func) {
    if(is_callable($func)) {
        return $func($num1, $num2);
    }
}

 

PHP already has built-in functions that accepts a closure like:

  • array_filter(array $arr, callable callback )
  • array_map(callable callback, array $arr)
  • array_walk(callable callback, array $arr)

 

Closures also can be returned from functions as shown in this example:

$calculate = function($x) {

    return function($y) use ($x) {
        return $x + $y;
    };
};

Now to invoke this closure:

$calculator = $calculate(4);   // This line return function

echo $calculator(20);

 Another common example when returning a closure is when you need to create factory functions.

 

Closure Class and anonymous functions 

Closure class in PHP represent the basis of anonymous functions and closures. Anonymous functions and closures are instances of the Closure class, and if you remember shortly from above example when checking the type of the $sum function, it has a type of Closure. 

 

Closure class terminology:

final class Closure {
    /* Methods */
    private __construct()
    public static bind(Closure $closure, ?object $newThis, object|string|null $newScope = "static"): ?Closure
    public bindTo(?object $newThis, object|string|null $newScope = "static"): ?Closure
    public call(object $newThis, mixed ...$args): mixed
    public static fromCallable(callable $callback): Closure
}

This class has methods that allow us to do nice things like converting a callable to anonymous function, bind closures to objects and more.

Converting a callback to anonymous function with Closure::fromCallable

<?php

function sayHello()
{
    echo "Hello";
}

$closure = Closure::fromCallable("sayHello");

var_dump($closure);

// object(Closure)#1 (1) {
  ["function"]=>
  string(8) "sayHello"
}

You see that the function type becomes of type Closure. Now let’s invoke the closure:

echo $closure();  // Hello

 

Bind closures to objects using bindTo() and bind() methods:

When bind a closure to objects a new anonymous function will be created with different bound object and new scope class. An example of this is to add new functionality to objects on the fly.

Let’s see this with an example: Suppose that we have this Circle class:

class Circle 
{
    protected float $radius;

    function __construct(float $radius)
    {
        $this->radius = $radius;
    }
}

And we need a way to calculate the area of the circle without modifying the original class. We can achieve this using closure binding like so:

$getArea = function() {
    $pi = 3.14159;
    return $this->radius * $this->radius * $pi;
};

In this code the anonymous function $getArea() have access to “$this“. But how the closure have access to “$this“, because we will bound this closure to class Circle like so:

$circle = new Circle(20);

$areaClosure = $getArea->bindTo($circle, "Circle");

echo $areaClosure();

I create new instance $circle of the Circle class, then because the $getArea()  instance of Closure class it has the bindTo() method.

The bindTo($newThis, $newScope) method accepts two arguments $newThis and $newScope and return a new anonymous function bound to the $newThis object and $newScope.

So in this example bindTo() bound $getArea to $circle object, now $this defined in the closure refers to $circle. The second parameter $newScope is important and it determines the visibility of the protected and private members and methods, if you omit it you cannot have access to private $radius and you will get an error.

From this example you see that closure binding is a nice feature and allows to add new functionality to existing classes. Also we see that how we can access to private and protected members.

 

The second method Closure::bind($closure, $newThis, $newScope) is a static version of bindTo() and it accepts the closure to bind, $newThis and $newScope and return a new anonymous function bound to new scope. 

In the above example we can achieve the same using Closure::bind()

$areaClosure = Closure::bind($getArea, $circle, "Circle");

echo $areaClosure();

 

First Class Callable

In PHP 8.1 a powerful feature added related to anonymous functions and callables which is the “First Class Callable Syntax”. With this feature you can create anonymous functions from callable just like Closure::fromCallable() but with shorter syntax. 

The new CallableExpr(...) syntax is used to create a Closure object from callable.

  • Create a callable from functions:
$arrayMerge = array_merge(...);
$strlen = strlen(...);
$explode = explode(...);

var_dump($arrayMerge, $strlen, $explode);

// object(Closure)#1 (2) {
  ["function"]=>
  string(11) "array_merge"
}
var_dump($arrayMerge([1,2], [6,10]));
  • Create a callable from class methods:
class Article
{
    private $title = '';

    public function setTitle(string $title)
    {
        $this->title = $title;
    }

    public function getTitle()
    {
        return $this->title;
    }

}

$article = new Article();

$setTitle = $article->setTitle(...);
$getTitle = $article->getTitle(...);

$setTitle('new article');
echo $getTitle();
  • Create a callable from method string:
$methodName = "setTitle";
$setTitle = $article->$methodName(...);
var_dump($setTitle);
  • Create a callable from static class method:
class Factory
{
    public static function getInstance()
    {
        return new self;
    }
}

$getInstance = Factory::getInstance(...);

var_dump($getInstance());
  • Create a callable from static class and method string:
$factory = "Factory";
$method = "getInstance";

$getInstance = $factory::$method(...);

var_dump($getInstance);

 

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments