PHP - PHP Closures and Variable Scope
A closure in PHP is an anonymous function that can capture variables from the surrounding scope and use them even when those variables are outside the function's normal local scope. Closures are useful when you need to create small, reusable pieces of functionality without defining a separate named function.
Closures are also commonly called anonymous functions when discussing PHP. However, the important feature of a closure is its ability to remember and access variables from the scope where it was created.
1. What Is a Closure?
A closure is a function without a declared name. It is usually assigned to a variable and can then be called through that variable.
$greeting = function () {
echo "Hello, World!";
};
$greeting();
Output:
Hello, World!
In this example:
-
function () { ... }creates an anonymous function. -
$greetingstores that function. -
$greeting()executes the function.
A closure can accept parameters just like a normal function.
$greet = function ($name) {
return "Hello, " . $name;
};
echo $greet("Rahul");
Output:
Hello, Rahul
2. Closures With Variables
One of the most useful features of closures is their ability to work with variables from an outer scope.
Consider this example:
$message = "Welcome";
$display = function () {
echo $message;
};
$display();
This does not work as expected because $message belongs to the outer scope, while the closure has its own local scope.
To make $message available inside the closure, PHP provides the use keyword.
$message = "Welcome";
$display = function () use ($message) {
echo $message;
};
$display();
Output:
Welcome
The use keyword tells PHP that the closure should capture the specified variable from the surrounding scope.
3. Using the use Keyword
The basic syntax is:
function () use ($variable) {
// Function body
}
For example:
$tax = 18;
$calculateTax = function ($price) use ($tax) {
return $price + ($price * $tax / 100);
};
echo $calculateTax(1000);
Output:
1180
Here, $tax is defined outside the closure. The use ($tax) statement makes its value available inside the closure.
4. Capturing Multiple Variables
A closure can capture more than one variable.
$tax = 18;
$discount = 10;
$calculate = function ($price) use ($tax, $discount) {
$afterDiscount = $price - ($price * $discount / 100);
return $afterDiscount + ($afterDiscount * $tax / 100);
};
echo $calculate(1000);
The closure captures both $tax and $discount.
The syntax is:
function () use ($variable1, $variable2, $variable3) {
// Function body
}
5. Closures Capture Values by Default
When a variable is imported using use, PHP normally captures its value at the time the closure is created.
$number = 10;
$showNumber = function () use ($number) {
echo $number;
};
$number = 20;
$showNumber();
Output:
10
Although $number was later changed to 20, the closure still uses the captured value 10.
This behavior is important when working with closures because the closure does not automatically follow subsequent changes to the original variable.
6. Capturing Variables by Reference
PHP allows a closure to capture a variable by reference using &.
$number = 10;
$increase = function () use (&$number) {
$number++;
};
$increase();
echo $number;
Output:
11
Here, &$number means that the closure works with the original variable rather than a separate captured value.
Compare this with value capture:
$number = 10;
$increase = function () use ($number) {
$number++;
};
$increase();
echo $number;
Output:
10
The original $number remains unchanged because the closure received a captured value.
7. Difference Between Value and Reference Capture
The distinction can be summarized as follows:
| Feature | Value Capture | Reference Capture |
|---|---|---|
| Syntax | use ($value) |
use (&$value) |
| Captures | Value | Reference |
| Changes original variable? | No | Yes |
| Reflects later changes to original variable? | Generally no | Yes |
| Useful for modifying outer variables? | No | Yes |
Reference capture should be used carefully because changes made inside the closure can affect variables outside it.
8. Closures and Function Parameters
Closures can be passed as arguments to other functions. This is one of their most important practical applications.
For example:
function calculate($number, $operation) {
return $operation($number);
}
$square = function ($number) {
return $number * $number;
};
echo calculate(5, $square);
Output:
25
Here, $square contains a closure. The closure is passed to calculate() as $operation.
The function then executes it:
$operation($number);
This allows a function to receive behavior as an argument.
9. Closures as Callback Functions
Closures are frequently used as callback functions.
A callback is a function that is passed to another function so that it can be executed later.
For example:
$numbers = [1, 2, 3, 4, 5];
$result = array_map(function ($number) {
return $number * 2;
}, $numbers);
print_r($result);
Output:
Array
(
[0] => 2
[1] => 4
[2] => 6
[3] => 8
[4] => 10
)
The closure is executed for every element of the array.
Another example is filtering data:
$numbers = [10, 15, 20, 25, 30];
$result = array_filter($numbers, function ($number) {
return $number > 20;
});
print_r($result);
The closure determines which values should remain in the resulting array.
10. Closures With array_map()
array_map() applies a callback to each element of an array.
$names = ["john", "mary", "david"];
$upperNames = array_map(function ($name) {
return strtoupper($name);
}, $names);
print_r($upperNames);
Output:
Array
(
[0] => JOHN
[1] => MARY
[2] => DAVID
)
The closure provides the processing logic without requiring a separate named function.
11. Closures With array_filter()
array_filter() can use a closure to determine which elements should be retained.
$ages = [12, 18, 25, 16, 30];
$adults = array_filter($ages, function ($age) {
return $age >= 18;
});
print_r($adults);
Output:
Array
(
[1] => 18
[2] => 25
[4] => 30
)
Notice that array_filter() preserves the original array keys. If sequential keys are required, array_values() can be used.
$adults = array_values($adults);
12. Closures With array_reduce()
Closures are also useful with array_reduce().
$numbers = [10, 20, 30, 40];
$total = array_reduce($numbers, function ($carry, $number) {
return $carry + $number;
}, 0);
echo $total;
Output:
100
Here:
-
$carrycontains the accumulated result. -
$numbercontains the current array element. -
0is the initial value.
13. Closures and Scope
Variable scope determines where a variable can be accessed.
Consider:
$name = "John";
function test() {
echo $name;
}
The function cannot directly access $name because $name belongs to the global scope.
Closures behave similarly:
$name = "John";
$show = function () {
echo $name;
};
The closure cannot automatically access $name.
Using use makes the variable available:
$name = "John";
$show = function () use ($name) {
echo $name;
};
$show();
Output:
John
Understanding this distinction is important when working with closures.
14. Closures and $this
When a closure is created inside an object method, it can work with the current object through $this, subject to PHP's closure binding rules.
For example:
class User
{
private string $name = "John";
public function getNameFunction()
{
return function () {
return $this->name;
};
}
}
$user = new User();
$getName = $user->getNameFunction();
echo $getName();
Output:
John
The closure created within the object method can access the object's $this context.
This is particularly useful when closures are used as callbacks inside class methods.
15. Binding Closures to Objects
PHP also provides methods such as Closure::bind() and Closure::call() for working with closure binding.
For example:
class User
{
private string $name = "John";
}
$user = new User();
$closure = function () {
return $this->name;
};
$bound = $closure->bindTo($user, User::class);
echo $bound();
The closure is bound to the $user object and can therefore access the object's private property in the appropriate scope.
This feature is powerful but should be used carefully because it can make code more difficult to understand if excessive dynamic binding is used.
16. Closures as Return Values
A function can return a closure.
function createMultiplier($factor)
{
return function ($number) use ($factor) {
return $number * $factor;
};
}
$double = createMultiplier(2);
$triple = createMultiplier(3);
echo $double(5);
echo "\n";
echo $triple(5);
Output:
10
15
This is a simple example of a closure remembering a value from the environment where it was created.
The $double closure remembers that $factor is 2, while $triple remembers that $factor is 3.
17. Practical Example: Creating a Discount Calculator
Closures can be used to create reusable business logic.
function createDiscountCalculator($discount)
{
return function ($price) use ($discount) {
return $price - ($price * $discount / 100);
};
}
$studentDiscount = createDiscountCalculator(10);
$festivalDiscount = createDiscountCalculator(20);
echo $studentDiscount(1000);
echo "\n";
echo $festivalDiscount(1000);
Output:
900
800
Each closure maintains its own captured discount value.
18. Closures vs Named Functions
A named function has a declared name:
function add($a, $b)
{
return $a + $b;
}
A closure can be stored in a variable:
$add = function ($a, $b) {
return $a + $b;
};
Named functions are generally appropriate when a piece of functionality is broadly reusable throughout an application.
Closures are especially useful when behavior is needed locally, temporarily, or as a callback.
19. Closures and Arrow Functions
PHP also provides arrow functions, which offer a shorter syntax for simple closures.
A normal closure:
$numbers = [1, 2, 3, 4];
$result = array_map(function ($number) {
return $number * 2;
}, $numbers);
The equivalent arrow function is:
$result = array_map(
fn($number) => $number * 2,
$numbers
);
Arrow functions automatically capture variables from the parent scope by value.
For example:
$factor = 10;
$multiply = fn($number) => $number * $factor;
echo $multiply(5);
Output:
50
Arrow functions are convenient for short expressions, while traditional closures provide more flexibility for multi-statement logic and explicit use declarations.
20. Advantages of Closures
Closures provide several benefits:
-
They allow functions to be created dynamically.
-
They can capture variables from their surrounding scope.
-
They are useful as callback functions.
-
They help keep small pieces of logic close to where they are used.
-
They can be returned from other functions.
-
They support functional programming techniques.
-
They are useful when processing arrays and collections.
-
They can encapsulate temporary behavior without creating additional named functions.
21. Limitations and Things to Consider
Closures should not be used everywhere simply because they are available.
Excessive use can make code harder to understand, particularly when many variables are captured or when closures are deeply nested.
For example:
$result = function () use ($a, $b, $c, $d, $e) {
// Complex logic
};
If a closure becomes large or represents an important reusable operation, creating a named function or class may make the code clearer.
Reference capture should also be used carefully:
function () use (&$value) {
$value++;
}
Because the closure can modify the external variable, it introduces a side effect that may make debugging more difficult.
22. Important Points to Remember
A PHP closure is an anonymous function that can be assigned to a variable, passed as an argument, returned from another function, or used as a callback.
The use keyword allows a closure to capture variables from its surrounding scope:
function () use ($variable) {
}
By default, the captured variable is imported by value.
To capture it by reference, use:
function () use (&$variable) {
}
Closures are particularly useful with functions such as array_map(), array_filter(), and array_reduce(). They are also useful for callbacks, dynamic behavior, event handling, and creating specialized functions.
Understanding closures and variable scope is important for writing modern PHP applications because they provide a flexible way to pass and preserve behavior while keeping related logic concise and organized.