beginner
Step 4 of 16
Functions and Arrow Functions
PHP Programming
Functions and Arrow Functions
Functions in PHP have evolved significantly with each major version. Modern PHP supports type declarations for parameters and return values, default parameter values, named arguments, variadic parameters, union types, and concise arrow functions. These features bring PHP closer to the expressiveness of languages like TypeScript while maintaining backward compatibility. Writing well-typed functions improves code reliability and makes your IDE significantly more helpful with autocompletion and error detection.
Function Basics
<?php
// Basic function
function greet(string $name): string {
return "Hello, $name!";
}
echo greet("Alice"); // "Hello, Alice!"
// Default parameters
function createUser(string $name, string $role = "user", bool $active = true): array {
return compact('name', 'role', 'active');
}
$user = createUser("Alice"); // role="user", active=true
$admin = createUser("Bob", "admin");
// Named arguments (PHP 8.0+)
$user = createUser(name: "Charlie", active: false, role: "editor");
// Order does not matter with named arguments!
// Nullable types
function findUser(?int $id): ?array {
if ($id === null) return null;
return ['id' => $id, 'name' => 'User ' . $id];
}
// Union types (PHP 8.0+)
function processInput(string|int $input): string {
return "Processed: " . (string)$input;
}
// Intersection types (PHP 8.1+)
function processItem(Countable&Iterator $collection): void {
// $collection must implement BOTH interfaces
}
Return Types and Variadic Parameters
<?php
// Void return type
function logMessage(string $message): void {
error_log("[" . date('Y-m-d H:i:s') . "] $message");
// Cannot return a value
}
// Never return type (PHP 8.1+) — function never returns normally
function throwError(string $message): never {
throw new RuntimeException($message);
}
// Variadic parameters
function sum(int|float ...$numbers): int|float {
return array_sum($numbers);
}
echo sum(1, 2, 3, 4, 5); // 15
// Spread operator
$nums = [1, 2, 3];
echo sum(...$nums); // 6
// Returning multiple values
function getMinMax(array $numbers): array {
return [min($numbers), max($numbers)];
}
[$min, $max] = getMinMax([3, 1, 4, 1, 5, 9]);
echo "Min: $min, Max: $max
"; // Min: 1, Max: 9
Arrow Functions and Closures
<?php
// Arrow functions (PHP 7.4+) — single expression, auto-captures variables
$multiplier = fn(int $x, int $y): int => $x * $y;
echo $multiplier(3, 4); // 12
// Arrow functions capture outer variables automatically
$tax_rate = 0.1;
$addTax = fn(float $price): float => $price * (1 + $tax_rate);
echo $addTax(100); // 110
// Traditional closures (anonymous functions)
$greeting = "Hello";
$greet = function(string $name) use ($greeting): string {
return "$greeting, $name!";
};
echo $greet("Alice"); // "Hello, Alice!"
// Closures with reference (can modify outer variable)
$counter = 0;
$increment = function() use (&$counter): int {
return ++$counter;
};
echo $increment(); // 1
echo $increment(); // 2
echo $counter; // 2 (modified!)
// Practical: sorting with closures
$users = [
['name' => 'Charlie', 'age' => 25],
['name' => 'Alice', 'age' => 30],
['name' => 'Bob', 'age' => 20],
];
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);
// Sorted by age ascending
// First-class callable syntax (PHP 8.1+)
$lengths = array_map(strlen(...), ["hello", "world", "php"]);
print_r($lengths); // [5, 5, 3]
?>
Higher-Order Functions
<?php
// Function that returns a function
function createValidator(string $type): Closure {
return match($type) {
'email' => fn(string $v): bool => filter_var($v, FILTER_VALIDATE_EMAIL) !== false,
'url' => fn(string $v): bool => filter_var($v, FILTER_VALIDATE_URL) !== false,
'number' => fn(string $v): bool => is_numeric($v),
default => fn(string $v): bool => !empty($v),
};
}
$validateEmail = createValidator('email');
echo $validateEmail('alice@example.com') ? 'Valid' : 'Invalid'; // Valid
echo $validateEmail('not-an-email') ? 'Valid' : 'Invalid'; // Invalid
// Pipeline pattern
function pipe(mixed $value, callable ...$functions): mixed {
foreach ($functions as $fn) {
$value = $fn($value);
}
return $value;
}
$result = pipe(
" Hello World ",
'trim',
'strtolower',
fn($s) => str_replace(' ', '-', $s)
);
echo $result; // "hello-world"
?>
Pro tip: Always add type declarations to your function parameters and return types. This catches bugs at runtime (or even at static analysis time with tools like PHPStan), improves IDE autocompletion, and serves as documentation. Use strict types at the top of your files: declare(strict_types=1);
Key Takeaways
- Modern PHP supports typed parameters, return types, union types, and nullable types for safer code.
- Named arguments (PHP 8.0) allow passing parameters in any order and improve readability.
- Arrow functions (
fn() =>) are concise and automatically capture outer variables; closures useuse(). - Use
declare(strict_types=1)to enforce type checking and catch type errors early. - First-class callable syntax (
strlen(...)) in PHP 8.1 makes passing functions as arguments cleaner.