Анонимные функции (Closures)
<?php
declare(strict_types=1);
// Anonymous function assigned to variable
$greet = function (string $name): string {
return "Hello, {$name}!";
};
echo $greet('Alice'); // Hello, Alice!
echo get_class($greet); // Closure
// As callback
$numbers = [1, 2, 3, 4, 5];
$doubled = array_map(function (int $n): int {
return $n * 2;
}, $numbers);
// Immediately invoked
$result = (function (): string {
return 'Immediate!';
})();
echo $result; // Immediate!
Захват переменных (use)
<?php
declare(strict_types=1);
$multiplier = 3;
// Capture by VALUE (default)
$multiply = function (int $n) use ($multiplier): int {
return $n * $multiplier;
};
$multiplier = 100; // Change after closure creation
echo $multiply(5); // 15 (NOT 500! Captured value at creation time)
// Capture by REFERENCE
$counter = 0;
$increment = function () use (&$counter): void {
$counter++;
};
$increment();
$increment();
echo $counter; // 2 — modified via reference!
// Multiple captures
$prefix = 'User';
$suffix = '!';
$format = function (string $name) use ($prefix, $suffix): string {
return "{$prefix}: {$name}{$suffix}";
};
echo $format('Alice'); // User: Alice!
Ловушка экзамена:
useзахватывает переменные по значению на момент создания замыкания (не вызова). Изменения переменной после создания НЕ видны внутри замыкания. Для "живой" связи используйтеuse (&$var).
<?php
declare(strict_types=1);
$value = 'original';
$fn = function () use ($value): string {
return $value;
};
$value = 'changed';
echo $fn(); // 'original' — captured at creation time!
// With reference
$fn2 = function () use (&$value): string {
return $value;
};
$value = 'changed again';
echo $fn2(); // 'changed again' — reference captures current value
Arrow Functions (PHP 7.4+)
<?php
declare(strict_types=1);
// Arrow function syntax
$double = fn(int $n): int => $n * 2;
echo $double(5); // 10
// Arrow functions automatically capture outer variables BY VALUE
$multiplier = 3;
$multiply = fn(int $n): int => $n * $multiplier;
echo $multiply(5); // 15
$multiplier = 100;
echo $multiply(5); // 15 — still captured by value at creation!
// Use in callbacks
$numbers = [1, 2, 3, 4, 5];
$even = array_filter($numbers, fn(int $n): bool => $n % 2 === 0);
// [1 => 2, 3 => 4]
// Chaining
$names = ['alice', 'bob', 'charlie'];
$result = array_map(
fn(string $name): string => ucfirst($name),
array_filter($names, fn(string $name): bool => strlen($name) > 3)
);
// Arrow functions are single-expression only
// fn($x) => { ... } // SYNTAX ERROR! No blocks allowed
// Use regular closures for multi-line logic
Отличия Arrow Functions от Closures
| Особенность | Closure | Arrow Function |
|---|---|---|
| Синтаксис | function() {} |
fn() => |
| Тело | Блок {} |
Одно выражение |
| Захват | Явный use |
Автоматический |
| Способ захвата | По значению или ссылке | Только по значению |
| Многострочный | Да | Нет |
return |
Явный | Неявный |
Ловушка экзамена: Arrow functions захватывают переменные ТОЛЬКО по значению. Нет способа захватить по ссылке в arrow function. Если нужна ссылка — используйте обычную closure с
use (&$var).
Closure::bind и Closure::bindTo
<?php
declare(strict_types=1);
class Wallet
{
private float $balance = 100.0;
}
// Bind closure to an object (access private members)
$getBalance = Closure::bind(
function (): float {
return $this->balance;
},
new Wallet(),
Wallet::class // Scope for accessing private members
);
echo $getBalance(); // 100.0
// bindTo — creates a new closure bound to object
$wallet = new Wallet();
$addMoney = function (float $amount): void {
$this->balance += $amount;
};
$bound = Closure::bind($addMoney, $wallet, Wallet::class);
$bound(50.0);
echo Closure::bind(
fn(): float => $this->balance,
$wallet,
Wallet::class
)(); // 150.0
// Practical use: helper for testing private methods
function callPrivate(object $obj, string $method, mixed ...$args): mixed
{
$closure = Closure::bind(
fn() => $this->$method(...$args),
$obj,
$obj::class
);
return $closure();
}
First-Class Callable Syntax (PHP 8.1+)
<?php
declare(strict_types=1);
// Create Closure from named function
$fn = strlen(...);
echo $fn('Hello'); // 5
// From static method
class Math
{
public static function square(int $n): int { return $n ** 2; }
public function cube(int $n): int { return $n ** 3; }
}
$square = Math::square(...);
echo $square(4); // 16
// From instance method
$math = new Math();
$cube = $math->cube(...);
echo $cube(3); // 27
// Practical examples
$numbers = [1, 2, 3, 4, 5];
// Before PHP 8.1:
$result = array_map('intval', $strings);
$result = array_filter($numbers, function (int $n): bool { return $n > 2; });
// PHP 8.1+:
$result = array_map(intval(...), $strings);
$result = array_filter($numbers, fn(int $n): bool => $n > 2);
// With built-in functions
$trimmed = array_map(trim(...), [' hello ', ' world ']);
$upper = array_map(strtoupper(...), ['hello', 'world']);
Генераторы (краткий обзор)
<?php
declare(strict_types=1);
// Generator function uses yield
function fibonacci(): Generator
{
[$a, $b] = [0, 1];
while (true) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
$fib = fibonacci();
echo $fib->current(); // 0
$fib->next();
echo $fib->current(); // 1
// Use in foreach (lazy iteration)
foreach (fibonacci() as $i => $num) {
if ($i >= 10) break;
echo "$num ";
}
// 0 1 1 2 3 5 8 13 21 34
// yield from — delegate to another generator
function inner(): Generator
{
yield 1;
yield 2;
return 'inner result';
}
function outer(): Generator
{
$result = yield from inner();
echo $result; // 'inner result'
yield 3;
}