Question

PHP Closures and the use Keyword Explained

phpclosures

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 $total by reference, allowing the callback to change the original $total variable.

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.”

Take Quiz

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:

  1. $tax is 0.20, and $total starts at 0.00.
  2. PHP creates the closure. It captures $tax and $prices by value, and $total by reference.
  3. array_walk() visits the first item. For 'book' => 2, it calls the closure with $quantity = 2 and $product = 'book'.
  4. The closure adds 10.00 × 2 × 1.20, which is 24.00, to the shared $total.
  5. array_walk() visits 'pen' => 3. The closure adds 2.00 × 3 × 1.20, which is 7.20.
  6. $total is now 31.20 outside 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;
    }
);
Take Quiz

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

FeatureWhat it doesBest use
Named functionDefines reusable behavior with a nameLogic used in many places
Anonymous functionDefines behavior without a nameA one-off callback
Closure with useAnonymous function that captures local variablesCallback needs surrounding context
use ($value)Captures the current valueStable configuration such as a tax rate
use (&$value)Shares the original variable by referenceDeliberate counters or accumulators
foreach loopIterates with visible local stateStraightforward processing and accumulation
array_reduce()Builds one result from array valuesFunctional-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.

Take Quiz

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.
  • use imports 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 in use.
  • 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.

Take Quiz

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.

Take Quiz

Keep learning

Related questions

View more