# PHP Closures and Variable Capture with use

> What closures (anonymous functions) are in PHP, how variable capture with the use keyword works, and practical everyday examples.

- Published: 2017-06-04
- Category: Languages
- Tags: PHP
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/php-closures-and-variable-capture-with-use/
- Language: en-US
- Author: Muhammet Şafak

---
**Closures** (anonymous functions) were introduced in PHP 5.3 — functions that can be assigned to a variable or passed as an argument to another function. Over time they became an indispensable part of my day-to-day PHP code. The more I used them for array operations and anywhere a callback was needed, the more I appreciated how practical they are.

That said, understanding closures alone is not enough; you also need to understand variable capture with `use`. Anyone who skips that part will struggle to figure out why an expected value is not visible inside the function.

## What is a closure?

In its simplest form: a function with no name that can be assigned to a variable.

```php
$greet = function (string $name): string {
    return 'Hello, ' . $name;
};

echo $greet('Ahmet'); // Hello, Ahmet
```

Here `$greet` holds a `Closure` object. You can pass it to another function:

```php
$names = ['Ahmet', 'Mehmet', 'Ayşe'];

$greeted = array_map(function (string $name) {
    return 'Hello, ' . $name;
}, $names);
```

Built-in functions like `array_map`, `array_filter`, and `usort` all accept a callback parameter — closures fit that role naturally.

## Variable capture with use

A closure does not automatically have access to variables in the surrounding scope. PHP behaves differently from JavaScript here. To use an outer variable inside a closure you must explicitly capture it with the `use` keyword.

```php
$discount = 0.10;

$applyDiscount = function (float $price) use ($discount): float {
    return $price * (1 - $discount);
};

echo $applyDiscount(100); // 90
```

Without `use ($discount)`, `$discount` would trigger an undefined variable error.

Once you understand why this design choice was made, it makes sense. PHP was designed to keep scope under control. If a closure could silently access every surrounding variable, tracking which external data a large function depended on would become a nightmare. The `use` list explicitly documents a closure's dependencies on the outside world.

## Capture by value vs. capture by reference

The default behaviour is **by-value** capture: the closure takes a snapshot of the variable's value at definition time, and later changes to that variable have no effect on the closure.

```php
$multiplier = 2;

$double = function (int $n) use ($multiplier): int {
    return $n * $multiplier;
};

$multiplier = 5; // This change does NOT affect $double

echo $double(10); // 20, because the value at definition time (2) is used
```

To capture by reference, write `use (&$multiplier)`. This is needed less often — typically when you want the closure to mutate state in the outer scope.

The classic use case for by-reference capture is a counter or accumulator pattern:

```php
$count = 0;
$increment = function () use (&$count): void {
    $count++;
};

$increment();
$increment();
echo $count; // 2
```

Use this pattern with care, though. A closure that mutates external state makes side effects harder to track. Returning a value and accumulating it outside is usually more readable.

## A real-world example: filtering arrays

In practice, the place I reach for this pattern most often is dynamic filtering — a filter criterion comes from outside and is captured inside the closure:

```php
function filterByStatus(array $orders, string $status): array
{
    return array_filter($orders, function (array $order) use ($status): bool {
        return $order['status'] === $status;
    });
}

$pendingOrders = filterByStatus($orders, 'pending');
```

Without capturing `$status` via `use`, accessing `$status` inside the closure would be impossible. This structure is both readable and reusable.

You can easily extend the same idea to multiple criteria:

```php
function filterOrders(array $orders, string $status, ?string $city = null): array
{
    return array_filter($orders, function (array $order) use ($status, $city): bool {
        if ($order['status'] !== $status) {
            return false;
        }
        if ($city !== null && $order['city'] !== $city) {
            return false;
        }
        return true;
    });
}
```

Multiple variables can appear in the `use` list, separated by commas.

## Passing a closure as a function parameter

Functions you write yourself can also accept a closure as a parameter. Use `callable` or `Closure` as the [type hint](/en/blog/scalar-type-declarations-and-return-types-in-php-7/):

```php
function applyToEach(array $items, Closure $fn): array
{
    $result = [];
    foreach ($items as $key => $item) {
        $result[$key] = $fn($item);
    }
    return $result;
}

$prices = [100, 200, 150];
$discounted = applyToEach($prices, function (float $price) {
    return $price * 0.9;
});
```

This structure lets you abstract the operation. The looping logic lives inside `applyToEach`; the transformation logic is injected from outside.

The difference between `callable` and `Closure`: `callable` also accepts string function names and `[$object, 'method']` arrays, while `Closure` only accepts anonymous functions. `Closure` is more restrictive from a type-safety standpoint, but if you intentionally expect only an anonymous function it expresses intent more clearly.

## PHP 7.4 and later: short closure syntax

Arrow functions, introduced in PHP 7.4, shortened the closure syntax considerably. For single-expression closures you use the `fn` keyword and drop `use` entirely — the outer scope is captured automatically:

```php
$discount = 0.10;
$applyDiscount = fn(float $price): float => $price * (1 - $discount);
```

When this post was originally written (2017), arrow functions did not yet exist in the language. Today this syntax is more common for simple single-expression closures. The `use`-based capture remains valid and necessary for multi-line closures.

The biggest pitfall when learning closures is skipping over the variable-capture mechanism entirely. Once you understand it, closures make array operations, event listeners, and anything else that needs a callback shorter and more intentional.
