A function is declared with function, a name, a parameter list and a body.
The manual’s Functions
chapter covers the same ground.
Declaration
function [&] name ( [parameter {, parameter} [,]] ) [: type] { statements }
parameter: {attribute} [type] [&] [...] $name [= default]
function greet(string $name, string $greeting = 'Hello'): string
{
return "$greeting, $name";
}
echo greet('Ada');
- Function names are case-insensitive. A function cannot be redeclared, and PHP has no overloading by parameter list.
- A function declared at the top level of a file can be called before its declaration. A function declared inside a condition or inside another function exists only once that code has run, and then in the global (or current namespace) scope.
- The type comes before the name of a parameter. Attributes come first of all. See Type declarations.
- A trailing comma after the last parameter is allowed since PHP 8.0, and after the last argument of a call since PHP 7.3.
Parameters
Arguments are evaluated from left to right before the call and passed by value unless
the parameter says otherwise. Too few arguments for the required parameters throws
ArgumentCountError (PHP 7.1). Extra arguments are ignored, but can be read
with func_get_args().
Default values
- A default is used only when the argument is not passed. Passing
nullpassesnull. - A default must be a
constant expression.
Since PHP 8.1 it may use
new. - A required parameter after an optional one is deprecated, because the default can
never be used.
Type $x = nullused to mark a parameter nullable as well; that implicit nullable form is deprecated since PHP 8.4.
Passing by reference
An & before the parameter name makes the parameter a
reference to the caller’s variable.
The caller writes nothing special.
function addOne(int &$n): void { $n++; }
$x = 1;
addOne($x); // $x is now 2
addOne(1); // Error: a literal cannot be passed by reference
A by-reference parameter may be given a default value. Passing an undefined variable creates it. Passing an array element creates it too.
Variadic parameters
A parameter written ...$name
(PHP 5.6) collects the remaining
arguments into an array. It must be the last parameter. A type applies to every
collected value, and it can be by reference.
function sum(int|float ...$numbers): int|float
{
return array_sum($numbers);
}
sum(1, 2, 3); // 6
Argument unpacking
... at a call site
(PHP 5.6) spreads an array or
Traversable into arguments. It may be used more than once. A positional
argument cannot follow an unpacked one. A string key in the unpacked array becomes a
named argument (PHP 8.1).
$pair = [1, 2];
sum(...$pair, ...[3]); // 6
greet(...['name' => 'Ada']); // the same as greet(name: 'Ada')
Named arguments
An argument may be passed by parameter name
(PHP 8.0), as
name: value. The name is a literal identifier. It can be a reserved word.
Named arguments may be in any order, and optional parameters can be skipped.
htmlspecialchars($text, double_encode: false);
greet(greeting: 'Hi', name: 'Ada');
- Named arguments must come after positional ones. The reverse is a compile error.
- An unknown name throws
Error, as does a name that sets a parameter already given. - Since PHP 8.1 named arguments may follow an unpacked array, provided they do not set an argument the unpacking already supplied.
- A variadic parameter collects unknown named arguments, with string keys.
- Parameter names are part of the interface, so renaming one breaks callers that use it. An overriding method may rename a parameter. A call by name then uses the names of the method actually called.
Returning values
return expr; ends the function. With no return, or a bare
return;, the result is null. To return several values, return
an array and destructure it.
An & before the function name returns a reference, which the caller
binds with $r = &f();. Return types are checked when the function
returns; see Return types.
Callables and variable functions
If a variable holds a name, a call on it calls the function of that name. Parentheses after any expression call its value.
| Form | Calls |
|---|---|
'strlen' | The function of that name |
[$obj, 'method'] | A method of an object |
['Class', 'method'], 'Class::method' | A static method |
$closure | A Closure object |
$invokable | An object with __invoke() |
A string or array callable is checked when it is called, from the scope of the caller.
is_callable() tests one, and call_user_func() and
call_user_func_array() call one. Language constructs, such as
echo, isset and include, cannot be called this
way. exit and die became callable functions in PHP 8.4.
Anonymous functions
function (…) { … }, with no name, is an expression that creates an object of
the class Closure. It can be stored, passed and called like any value.
A semicolon is needed after an assignment of one.
[static] function [&] ( parameters ) [use ( [&]$var {, [&]$var} [,] )] [: type] { statements }
$factor = 3;
$times = function (int $n) use ($factor): int {
return $n * $factor;
};
$factor = 100;
echo $times(2); // 6: the value of $factor was captured when the closure was created
$total = 0;
$add = function (int $n) use (&$total): void { $total += $n; };
$add(5);
echo $total; // 5: captured by reference
- Variables from the parent scope are visible only if listed in
use. By default each is captured by value when the closure is created. - The parent scope is where the closure is declared, not where it is called.
- Since PHP 7.1 a
uselist may not name a superglobal,$thisor a variable with the same name as a parameter. - A closure created inside a class method is automatically bound to the object, so
$thisworks inside it. A closure declared asstatichas no$this. Closure::bind(),bindTo()andcall()rebind a closure to another object and class scope.Closure::fromCallable()makes a closure from a callable.- Since PHP 8.5
Closure::getCurrent()gives the running closure, which allows a closure to recurse without capturing itself. - Since PHP 8.5 static closures may be used in constant expressions.
Arrow functions
fn (parameters) => expression
(PHP 7.4) is a short closure
whose body is one expression, and whose value is the return value.
$y = 1;
$add = fn($x) => $x + $y;
$curry = fn($x) => fn($z) => $x * $z + $y;
echo $curry(5)(10); // 51
array_map(fn(int $n): int => $n * 2, [1, 2, 3]);
- Variables from the enclosing scope are captured automatically, by value, when the arrow function is created. Nested arrow functions capture through each level.
- Because capture is by value, an arrow function cannot change an outer variable.
Use an anonymous function with
use (&$x). - Parameters, return types, defaults, variadics, and by-reference parameters and
returns are all allowed:
fn&(array &$a) => $a. fnis reserved, and has very low precedence, so the body extends as far as it can.
First-class callables
Writing ... in place of the arguments of a call
(PHP 8.1) creates a
Closure without calling. The ... is literal syntax.
$f1 = strlen(...);
$f2 = $obj->method(...);
$f3 = Foo::staticMethod(...);
$f4 = $invokable(...);
echo $f1('abc'); // 3
- The closure uses the scope where it was made. A private method obtained this way can be called from outside the class, unlike a string or array callable, which is checked at the point of the call.
new Foo(...)is not allowed. Combining with the nullsafe operator is a compile error.- Since PHP 8.5 first-class callables can appear in constant expressions.
Generators
A function whose body contains yield is a
generator function (PHP 5.5). Calling it
runs none of its body and returns a Generator, an iterator that
runs the body up to each yield and then pauses.
function evens(int $max): Generator
{
for ($n = 0; $n <= $max; $n += 2) {
yield $n;
}
return 'done';
}
foreach (evens(4) as $value) {
echo $value; // 0, then 2, then 4
}
| Form | Meaning |
|---|---|
yield $v; | Yields a value with the next automatic integer key |
yield $k => $v; | Yields a key and a value |
yield; | Yields null with an automatic key |
$x = yield $v; | Receives a value passed to send() |
yield from $iterable; | Delegates to an array, a Traversable or a generator (PHP 7.0) |
function &gen() { yield $v; } | Yields by reference. Use foreach (gen() as &$x) |
GeneratorimplementsIteratorand addssend(),throw()andgetReturn().returnin a generator sets the value read bygetReturn()and does not yield it (PHP 7.0).yield fromkeeps the keys of its source, so keys can repeat. Passingfalseas the second argument ofiterator_to_array()avoids collisions.send()sets the result of the currentyieldand resumes.- A generator that has already started cannot be rewound, and one that has finished cannot be iterated again.
- Since PHP 7.0
yieldno longer needs parentheses in simple assignments. In larger expressions, parentheses avoid surprises, because of its low precedence.