Step 1 of 5 · Reading · ~3 min
Learn
Functions
Closures and Arrow Functions
Sometimes a function is too small to deserve a name, and what you really want is to hand a piece of behaviour to something else — a sort, a filter, a transformation. PHP calls those values closures, and they live in ordinary variables.
A function in a variable
<?php
$double = function ($n) {
return $n * 2;
};
echo $double(5), "\n";
//> 10
Note the semicolon after the closing brace. This is an assignment statement, not a function declaration.
Closures do not capture automatically
This is the rule that catches everyone. A closure's body starts with the same empty scope a named function gets. To pull an outer variable in, you list it in a use clause.
<?php
$rate = 1.5;
// ✗ $rate is undefined inside — the multiplication has nothing to work with
$broken = function ($kg) { return $kg * $rate; };
// ✓ opt in, explicitly
$byWeight = function ($kg) use ($rate) { return $kg * $rate; };
echo $byWeight(4), "\n";
//> 6
use ($rate) copies the value at the moment the closure is created. use (&$rate) shares the variable itself, which is how you build a counter:
<?php
$count = 0;
$tick = function () use (&$count) { $count++; };
$tick(); $tick(); $tick();
echo $count, "\n";
//> 3
Arrow functions — shorter, and they capture for you
PHP 7.4 added fn, and this course's grader runs 7.4, so you can use it in the exercises.
<?php
$rate = 1.5;
$byWeight = fn($kg) => $kg * $rate; // no use clause needed
echo $byWeight(4), "\n";
//> 6
function () use (...) { ... } | fn(...) => ... | |
|---|---|---|
| Body | Many statements | Exactly one expression |
| Return | An explicit return | Implicit — the expression is the result |
| Capture | A manual use list | Automatic, by value |
| By reference | use (&$x) is available | Not available |
Because the capture is by value and happens when the arrow function is defined, later changes to the outer variable never reach it:
<?php
$factor = 2;
$snap = fn($n) => $n * $factor;
$factor = 10;
echo $snap(5), "\n";
//> 10
Passing behaviour into a function
A parameter typed callable accepts anything callable — a closure, an arrow function, or the name of a built-in function as a plain string. You invoke it by putting parentheses straight after the variable.
<?php
function applyTwice(int $n, callable $op): int {
return $op($op($n));
}
echo applyTwice(2, fn($x) => $x * $x), "\n";
echo applyTwice(2, fn($x) => $x + 3), "\n";
echo applyTwice(3, 'abs'), "\n";
//> 16
//> 8
//> 3
This is the entire reason closures exist. usort is the classic example: you supply the comparison, PHP supplies the sorting.
<?php
$parcels = [["Porto", 5], ["Lisbon", 12], ["Faro", 1]];
usort($parcels, fn($a, $b) => $b[1] <=> $a[1]);
foreach ($parcels as $p) { echo $p[0], " ", $p[1], "\n"; }
//> Lisbon 12
//> Porto 5
//> Faro 1
<=> is the spaceship operator: it returns a negative number, zero, or a positive number, which is exactly the contract a comparison callback has to satisfy.
Grader note: PHP 8.1 added first-class callable syntax — writing
strlen(...)to get a callable value — and PHP 8.0 added named arguments. Neither parses on this course's PHP 7.4 grader. Usefn,function () use (...), or a plain'functionName'string.
Your exercise
Apply Twice asks you to complete function applyTwice(int $n, callable $op): int so that it runs $op on $n, then runs $op again on that result.
The starter passes in fn($x) => $x * $x, so squaring twice has to produce the fourth power. The mistake the grader catches is applying the operation once and then scaling: for the first test's input of 2, $op($n) on its own returns 4 and $op($n) * 2 returns 8, where 16 is expected. The correct body nests the call inside itself, and it prints nothing — the starter's echo already does that for you.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…