Functions
Function declarations, closures, arrow functions, variadic parameters, and more.
Declaration and calls
<?php
function add($a, $b) {
return $a + $b;
}
echo add(3, 4); // 7
Function lookup is case-insensitive like PHP. The declaration keeps its original
name, but calls such as ADD(3, 4), Add(3, 4), and add(3, 4) resolve to the
same function. Built-in function names follow the same rule.
Parameter and return type hints
<?php
function repeat(string $label, int $count): string {
return $label . $count;
}
- Supported types:
int,float,bool,string,array,mixed,iterable,callable,ptr,buffer<T>, class/interface/enum names, unions, and nullable forms voidis valid only as a return typeneveris valid only as a return type and must not return normally- Typed parameters can use default values
- Function, method, constructor, closure, and arrow-function parameter hints are checked
- Function, method, closure, and arrow-function return type hints are checked
- Variadic parameters are supported only without a parameter type hint for now;
function f(int ...$xs), typed variadic methods, typed variadic closures, and typed variadic arrow functions are rejected. - Non-
voiddeclared return types must return a value on every reachable path;throw,exit()/die(), and infinite loops count as non-returning paths - Bare
return;is valid only forvoidreturns; usereturn null;for nullable return types - Named arguments are supported for known-signature calls: user-defined functions, methods, closures, built-ins, and extern functions
- Argument expressions are evaluated in PHP source order, then codegen normalizes the resulting values into ABI parameter order
- Named arguments can follow spread arguments, as in
foo(...$args, suffix: "!"); positional arguments cannot follow either named arguments or spread arguments - Associative-array unpacking maps string keys to named arguments (
foo(...["name" => "Ada"])) and keeps numeric keys positional. Variable associative-array spreads can satisfy any parameter by string key, including parameters after explicit named arguments. Duplicate static string keys use PHP’s last-wins behavior before argument planning. - A positional spread into a variadic function fills regular parameters first; only excess spread elements are collected into the variadic parameter. If a spread is too short to fill required parameters, the call fails instead of reading beyond the array payload.
- User-defined variadic functions collect unknown named arguments into the variadic parameter using string keys
- Built-in variadic functions reject unknown named arguments, matching PHP’s internal-function behavior
Recursion
<?php
function factorial($n) {
if ($n <= 1) { return 1; }
return $n * factorial($n - 1);
}
echo factorial(10); // 3628800
Default parameter values
<?php
function greet($name = "world") {
echo "Hello " . $name . "\n";
}
greet(); // Hello world
greet("PHP"); // Hello PHP
Parameters with defaults must come after required parameters.
Local scope
Variables inside a function are separate from the caller.
Anonymous functions (closures)
<?php
$double = function(int $x): int {
return $x * 2;
};
echo $double(5); // 10
Closures can capture with use:
<?php
$factor = 3;
$multiply = function(int $x) use ($factor): int {
return $x * $factor;
};
echo $multiply(5); // 15
Use & in the capture list when the closure must share and mutate the outer
variable, including recursive anonymous functions:
<?php
$factorial = null;
$factorial = function($n) use (&$factorial) {
return $n <= 1 ? 1 : $n * $factorial($n - 1);
};
echo $factorial(5); // 120
Captured closures can also be used as callback values:
<?php
$factor = 3;
$values = array_map(function(int $x) use ($factor): int {
return $x * $factor;
}, [1, 2, 3]);
echo $values[2]; // 9
Static closures
A closure prefixed with static does not capture $this from its enclosing
scope. This matches PHP’s static function () {} and static fn () => ... —
useful when a closure is meant to be unbound (often paired with
Closure::bind(..., null, ...)):
<?php
$add = static function ($a, $b) {
return $a + $b;
};
echo $add(3, 4); // 7
$double = static fn ($x) => $x * 2;
echo $double(5); // 10
Inside a static closure, referencing $this is a compile-time error:
<?php
class C {
public int $count = 5;
public function bad() {
// Error: Cannot use $this inside a static closure
return static function () { return $this->count; };
}
}
Arrow functions
<?php
$double = fn(int $x): int => $x * 2;
echo $double(5); // 10
$nums = [1, 2, 3, 4];
$squared = array_map(fn(int $n): int => $n * $n, $nums);
First-class callable syntax
<?php
$triple = triple(...);
$double = MathBox::double(...);
Supported: user-defined function names, extern function names, ClassName::method(...), self::method(...), parent::method(...), and registered builtin wrappers. Builtin wrapper coverage includes common string transforms and searches (strlen(...), trim(...), substr(...), str_contains(...)), casts and type checks (intval(...), floatval(...), gettype(...), is_int(...)), math helpers (abs(...), sqrt(...), round(...)), JSON helpers, and array helpers including count(...), array_sum(...), array_product(...), and by-reference mutators such as sort(...).
Also supported: static::method(...) inside class methods, preserving late static binding for direct callable calls, and $obj->method(...) / $this->method(...) with either a local receiver variable or a non-local receiver expression such as (new Greeter("Hi "))->greet(...).
<?php
class Greeter {
public function hello($name) {
return "Hello " . $name;
}
}
$greeter = new Greeter();
$hello = $greeter->hello(...);
echo $hello("Ada"); // Hello Ada
Captured first-class callable targets (static::method(...) and $obj->method(...)) can be called directly through a local callable variable or as an immediate callable expression such as ($obj->method(...))("Ada"). They can also be passed to callback paths that forward captured callable environments, including array_map(), array_filter(), array_reduce(), array_walk(), usort(), uksort(), uasort(), call_user_func(), and call_user_func_array(). For by-reference callback parameters, call_user_func_array() supports literal argument arrays whose by-reference positions are variables, such as call_user_func_array($cb, [$value]). PHP disallows nullsafe first-class callable syntax ($obj?->method(...)), and elephc reports the same error.
Global variables
<?php
$x = 10;
function test() {
global $x;
echo $x; // 10
}
Static variables
<?php
function counter() {
static $n = 0;
$n++;
echo $n . "\n";
}
counter(); // 1
counter(); // 2
Pass by reference
<?php
function increment(&$val) {
$val++;
}
$x = 5;
increment($x);
echo $x; // 6
Variadic functions
<?php
function sum(...$nums) {
$total = 0;
foreach ($nums as $n) {
$total += $n;
}
return $total;
}
echo sum(1, 2, 3); // 6
Variadic parameters can appear on functions, methods, closures, and arrow
functions, but the variadic parameter itself cannot carry a type hint yet. Use
function sum(...$nums) instead of function sum(int ...$nums).
Spread operator
<?php
$args = [10, 20, 30];
echo sum(...$args); // 60
$a = [1, 2];
$b = [3, 4];
$c = [...$a, ...$b]; // [1, 2, 3, 4]
Call unpacking follows PHP’s parameter mapping. Spread values fill regular positional parameters before any variadic tail is built, and associative-array spreads treat string keys as named arguments:
<?php
function show($a, $b = 99, ...$rest) {
echo $a . ":" . $b . ":" . count($rest);
}
show(...[10]); // 10:99:0
show(...[10, 20, 30]); // 10:20:1
show(...[10, "b" => 20]); // 10:20:0
$args = ["b" => 20, "a" => 10];
show(...$args); // 10:20:0
$args = ["b" => 20];
show(...$args, a: 10); // 10:20:0
echo
echo is a PHP language construct statement. It writes each operand to stdout
using PHP scalar output rules and accepts PHP-compatible comma-separated
operands:
<?php
echo "Hello", ", ", "World!\n";
print is a PHP language construct expression. It writes its operand to stdout
using the same scalar output rules as echo, then returns 1.
<?php
$ok = print "ready\n";
echo $ok; // 1
echo print "nested"; // prints "nested1"
As in PHP, print can also stand alone as a statement:
<?php
print "hello\n";