Question
In PHP 5.3, what does this anonymous function syntax mean, particularly the use ($tax, &$total) clause?
public function getTotal($tax)
{
$total = 0.00;
$callback = function ($quantity, $product) use ($tax, &$total) {
$pricePerItem = constant(
__CLASS__ . '::PRICE_' . strtoupper($product)
);
$total += ($pricePerItem * $quantity) * ($tax + 1.0);
};
array_walk($this->products, $callback);
return round($total, 2);
}
What is a closure in PHP, why is use needed here, and when is this pattern appropriate to use?
Short Answer
A PHP closure is an anonymous function that can be stored in a variable, passed to another function, and executed later. The use clause explicitly gives the closure access to variables from the surrounding scope. In this example, $tax is copied into the closure and $total is imported by reference so the callback can update it.
Concept
A closure is an anonymous function together with the variables it captures from the scope where it was created.
Anonymous functions are useful as callbacks: small pieces of behavior passed to functions such as array_walk, array_filter, and usort.
PHP functions have their own local scope. Therefore, a variable created outside an anonymous function is not automatically available inside it:
$tax = 0.20;
$calculate = function ($price) {
// $tax is not available here.
return $price * (1 + $tax);
};
The use clause solves this by importing selected variables from the surrounding scope:
$calculate = function ($price) use ($tax) {
return $price * (1 + $tax);
};
In the original code:
use ($tax)captures the current value of$tax.use (&$total)captures$totalby reference, allowing the callback to change the original$totalvariable.
This matters because callbacks often need outside configuration, such as a tax rate, a search term, or a limit. Occasionally, they also need to accumulate a result. Closures let that behavior and its required context travel together.
Mental Model
Think of an anonymous function as a worker sent to process each item in a list.
The worker has its own workspace, so it cannot automatically see everything on your desk outside the workspace. The use clause is the set of items you put into the worker's bag before sending it out.
use ($tax)puts a photocopy of the tax rate in the bag.use (&$total)gives the worker access to the same shared tally sheet. Each time it processes an item, it can update the original total.
The & means “share this variable,” rather than “make a copy of its current value.”
Syntax and Examples
The general closure syntax is:
$function = function (parameters) use (outerVariables) {
// function body
};
A closure can then be called like a normal function:
$greeting = 'Hello';
$welcome = function ($name) use ($greeting) {
return $greeting . ', ' . $name . '!';
};
echo $welcome('Ava'); // Hello, Ava!
$greeting is defined outside the closure, so it must be listed in use.
Capture by value
Without &, PHP captures the variable's value at the time the closure is created:
$discount = 0.10;
$finalPrice = function ($price) use ($discount) {
return $price * (1 - $discount);
};
$discount = 0.50;
echo $finalPrice(100); // 90
The closure retained 0.10, not the later 0.50.
Capture by reference
Add & when the closure must see later changes or change the outer variable itself:
$count = 0;
$increment = function () use (&$count) {
$count++;
};
$increment();
$increment();
echo $count; // 2
In the question's code, array_walk() calls the closure for every product entry. The callback uses $tax as fixed input and updates the shared $total.
Step by Step Execution
Consider this smaller example:
$tax = 0.20;
$total = 0.00;
$items = [
'book' => 2,
'pen' => 3,
];
$prices = [
'book' => 10.00,
'pen' => 2.00,
];
array_walk($items, function ($quantity, $product) use ($tax, &$total, $prices) {
$total += $prices[$product] * $quantity * (1 + $tax);
});
echo $total; // 31.2
Execution proceeds as follows:
$taxis0.20, and$totalstarts at0.00.- PHP creates the closure. It captures
$taxand$pricesby value, and$totalby reference. array_walk()visits the first item. For'book' => 2, it calls the closure with$quantity = 2and$product = 'book'.- The closure adds
10.00 × 2 × 1.20, which is24.00, to the shared$total. array_walk()visits'pen' => 3. The closure adds2.00 × 3 × 1.20, which is7.20.$totalis now31.20outside the closure too, because it was captured with&.
The order of array_walk() callback parameters is important: it passes the array value first and the array key second. This example stores quantities as values and product names as keys.
Real World Use Cases
Closures with use are common when a callback needs context from its surrounding code.
- Filtering search results: Capture a minimum price or selected category.
- Sorting: Capture a configuration setting that controls sort behavior.
- Validation: Capture allowed values, a current user, or a validation rule.
- Data transformation: Capture exchange rates, tax rates, or formatting options while mapping records.
- Event handlers: Capture an ID or service needed when an event occurs later.
- Retries and error handling: Capture an operation's context for a callback that runs after a failure.
For example, filter products using a user-selected maximum price:
$maxPrice = 50;
$affordableProducts = array_filter(
$products,
function (array $product) use ($maxPrice) {
return $product['price'] <= $maxPrice;
}
);
Real Codebase Usage
In production PHP code, closures are often short, focused callbacks. Capturing configuration by value is especially common:
$allowedRoles = ['admin', 'editor'];
$canAccess = function (string $role) use ($allowedRoles): bool {
return in_array($role, $allowedRoles, true);
};
A common pattern is a guard clause inside a callback, which skips invalid records early:
$activeEmails = array_filter($users, function (array $user): bool {
if (!isset($user['email'])) {
return false;
}
return $user['active'] === true;
});
When transforming data, prefer returning a value from array_map() rather than mutating an outside variable by reference:
$taxRate = 0.20;
$pricesWithTax = array_map(function (float $price) use ($taxRate): float {
return $price * (1 + $taxRate);
}, $prices);
Capturing by reference is valid, but it creates shared mutable state. Use it deliberately—for example, when accumulating a total during iteration—and keep the closure small. In newer PHP code, a foreach loop or array_reduce() can sometimes make an accumulation easier to read.
Common Mistakes
Forgetting use
Outer local variables are not automatically in a closure's scope.
$rate = 0.20;
$withTax = function ($price) {
return $price * (1 + $rate); // Undefined variable $rate
};
Fix it by importing the variable:
$withTax = function ($price) use ($rate) {
return $price * (1 + $rate);
};
Expecting a by-value capture to change later
$rate = 0.10;
$calculate = function ($price) use ($rate) {
return $price * (1 + $rate);
};
$rate = 0.20;
echo $calculate(100); // 110, not 120
A by-value capture takes the value when the closure is created. Use use (&$rate) only if the closure truly needs the current shared variable.
Forgetting & when updating an outer total
$total = 0;
$add = function ($amount) use ($total) {
$total += $amount;
};
$add(5);
echo $total; // 0
The closure updated its own captured copy. Use use (&$total) to update the outer variable.
Overusing references
References can make code harder to reason about because multiple places can change the same variable. Prefer a returned result when practical.
Mixing up array_walk() arguments
array_walk() calls its callback as function ($value, $key). Ensure the parameter names and array structure match that order.
Capturing a variable that is not defined yet
The variable must exist before the closure is created:
$callback = function () use ($message) {
return $message;
};
$message = 'Hi'; // Too late
Define $message first.
Comparisons
| Feature | What it does | Best use |
|---|---|---|
| Named function | Defines reusable behavior with a name | Logic used in many places |
| Anonymous function | Defines behavior without a name | A one-off callback |
Closure with use | Anonymous function that captures local variables | Callback needs surrounding context |
use ($value) | Captures the current value | Stable configuration such as a tax rate |
use (&$value) | Shares the original variable by reference | Deliberate counters or accumulators |
foreach loop | Iterates with visible local state | Straightforward processing and accumulation |
array_reduce() | Builds one result from array values | Functional-style totals and summaries |
For the total-calculation example, array_reduce() can avoid a reference:
$total = array_reduce(
$this->products,
function (float $runningTotal, int $quantity, string $product) use ($tax): float {
$pricePerItem = constant(__CLASS__ . '::PRICE_' . strtoupper($product));
return $runningTotal + ($pricePerItem * $quantity) * (1 + $tax);
},
0.00
);
However, a simple foreach is often clearest for beginners and maintenance work.
Cheat Sheet
// Create an anonymous function
$callback = function ($value) {
return $value * 2;
};
// Capture an outer variable by value
$callback = function ($value) use ($multiplier) {
return $value * $multiplier;
};
// Capture an outer variable by reference
$callback = function ($value) use (&$total) {
$total += $value;
};
- A closure is an anonymous function that captures surrounding variables.
useimports local variables from the enclosing scope.use ($name)captures the value when the closure is created.use (&$name)shares the original variable and permits outer-state changes.- Variables must be defined before creating the closure.
- Closure parameters, such as
function ($value), are separate from captured variables inuse. array_walk()callback order is(value, key).- Prefer returning values over reference-based mutation when it improves clarity.
FAQ
What is a closure in PHP?
A closure is an anonymous function that can capture variables from the scope where it is created. It can be stored in a variable, passed as a callback, and called later.
Why does PHP use the use keyword with closures?
PHP closures have their own scope. use explicitly imports selected variables from the surrounding local scope into the closure.
What is the difference between use ($variable) and use (&$variable)?
Without &, the closure receives a captured value. With &, it shares the original variable by reference, so changes made inside the closure affect the outside variable.
Is use the same as importing a namespace in PHP?
No. Namespace imports use use at file or namespace scope, such as use App\Service\Mailer;. Closure use appears after a function's parameter list and captures local variables. They are different language features that use the same keyword.
Can a closure access global variables without use?
Not automatically. A closure can use the global keyword, but explicitly capturing required variables with use is generally clearer and easier to test.
Should I always use & when capturing a variable?
No. Use & only when shared mutation is necessary. Capturing by value is safer because the callback cannot unexpectedly modify outside state.
Is a closure evil or bad practice in PHP?
No. Closures are a standard, useful tool for callbacks and localized behavior. They become difficult to maintain only when they are very long, capture too much state, or rely heavily on references.
Mini Project
Description
Build a small invoice calculator that uses a closure to add tax to line items. The project demonstrates capturing a fixed tax rate by value and calculating a total without changing global state.
Goal
Calculate the tax-inclusive total for a list of invoice items using array_reduce() and a closure.
Requirements
Define an array of invoice items containing a name, quantity, and unit price.
Store the tax rate in a variable outside the closure.
Use a closure that captures the tax rate with use.
Calculate and display the final total rounded to two decimal places.
Do not use a global variable.
Keep learning
Related questions
Are PDO Prepared Statements Enough to Prevent SQL Injection in PHP?
Learn how PDO prepared statements prevent SQL injection in PHP, what they protect, and the mistakes that still leave MySQL apps vulnerable.
Can You Bind an Array to an IN Clause in PHP PDO?
Learn how PDO handles placeholders in IN() clauses, why arrays cannot be bound directly, and the safe PHP pattern to build dynamic queries.
Choosing the Right MySQL Collation for PHP and UTF-8
Learn how MySQL character sets and collations work with PHP, and how to choose a practical UTF-8 setup for web applications.