Closures have been introduced in PHP 5.3 and their most important use is for callback functions. Basically a closure in PHP is a function that can be created without a specified name - an anonymous function. Here's a closure function created as the second parameter of array_walk()
. By specifying the $v
parameter as a reference one can modify each value in the original array through the closure function.
<?php
$array = array('Ruby', 'PHP', 'JavaScript', 'HTML');
array_walk($array, function(&$v, $k) {
$v = $v . ' - Programming Language';
});
print_r($array);
You can define a closure as the value of a variable with a normal assignment. For example, the snippet below will print out 'I am a closure'.
<?php
$var = function() {
return 'I am a ' . func_get_arg(0);
};
print_r($var('Closure'));
Don't forget to end the assignment with a semicolon just like you would do with any variable definition ... I always forget so I thought it would help if I mentioned this :).
One of the most confusing aspects for these anonymous functions in PHP is related to how closures can make use of variables from their immediate parent scope (not necessarily the global scope). This is done with the use
language construct so that the syntax looks like this: function() use ($variable) { /* code */ }
.
In the example below, the closure will use / inherit the value of $param
from its parent scope which is the scope of the sayHello()
function, NOT the global scope and therefor the script will output Hi, I am Michael!
.
<?php
$param = 'John!';
function sayHello()
{
$param = 'Michael!';
$func = function() use ($param)
{
echo 'Hi, I am ' . $param;
};
$func();
}
sayHello();
If we were to modify the value of $param
inside the closure this would not affect it's value inside the parent scope unless we would 'use a reference'. In the two examples below, one with $param
as a reference and one as a normal (copied) variable the results are different.
This prints 'I am Michael!'
because $param
is not modified in its parent scope. In other words, the only place where $param='Dave!'
is inside the closure.
<?php
$param = 'John!';
function sayHello()
{
$param = 'Michael!';
$func = function() use ($param)
{
$param = 'Dave!';
};
$func();
echo 'I am ' . $param; // prints I am Michael!
}
sayHello();
The code below will print 'I am Dave!'
because $param
is 'used' as a reference.
<?php
$param = 'John!';
function sayHello()
{
$param = 'Michael!';
$func = function() use (&$param)
{
$param = 'Dave!';
};
$func();
echo 'I am ' . $param; // prints I am Dave!
}
sayHello();
Starting with version 7.4 PHP has the ability to interpret "arrow functions". Besides the obvious advatage of being shorter and more readable than classic anonymous functions, arrow functions can access variables from the parent scope through a technique called "implicit by-value scope binding"
Below there's a comparison between how a clasic closure behaves compared with a short closure.
<?php
//classic closure
$n = "Mike";
$calc = function() {
return "Hello " . $n;
}
$calc(); // undefined variable $n
?>
<?php
//short closure
$calc = fn() => "Hello " . $n;
$calc(); //returns Hello Mike
?>
A few gotchas:
Arrow functions can only have one expression - the return statement (as opposed to JavaScript)
The outer scope variables are accessible by value only so there's no way of modify them
A few gotchas:
Arrow functions can only have one expression - the return statement (as opposed to JavaScript)
The outer scope variables are accessible by value only so there's no way of modify them
To know more about PHP Closures and key concepts that developers should know, check out this PHP Hiring Guide by Toptal.