Lab 05: Functions & Closures
Objective
Define and call functions with typed parameters, default values, variadic args, and return types. Write closures and arrow functions. Use first-class callable syntax and higher-order functions.
Background
PHP 8 has a mature function system with union types, named arguments, readonly properties, and first-class callable syntax. Closures are objects in PHP (Closure class) — they can be stored, passed, and bound to different objects. Understanding PHP's function capabilities is essential for modern PHP frameworks like Laravel and Symfony.
Time
35 minutes
Prerequisites
Lab 04 (Control Flow)
Tools
PHP 8.3 CLI
Docker image:
zchencow/innozverse-php:latest
Lab Instructions
Step 1: Function Basics & Type Declarations
💡
declare(strict_types=1)at the top of a file enables strict type checking — passing afloatto anintparameter throws aTypeErrorinstead of silently converting. Always use it in modern PHP code to catch type errors early.
📸 Verified Output:
Step 2: Named Arguments & Variadic Functions
💡 Named arguments (PHP 8.0+) let you skip optional parameters and document intent.
createUser(name: 'Alice', role: 'admin')is clearer thancreateUser('Alice', 0, 'admin', true). They also make function calls resilient to parameter reordering.
📸 Verified Output:
Step 3: Closures
💡
usecaptures by value by default. If the outer variable changes after the closure is defined, the closure still sees the original value. Useuse (&$var)to capture by reference — then both the closure and outer scope share the same variable.
📸 Verified Output:
Step 4: Arrow Functions
💡 Arrow functions (
fn) automatically capture outer variables by value withoutuse— they read the enclosing scope implicitly. They're single-expression (no{}), alwaysreturnthe expression result, and can't modify outer variables (no&capture). Perfect for short callbacks.
📸 Verified Output:
Step 5: Higher-Order Functions
💡
compose(f, g, h)($x)=f(g(h($x)))— right to left like math.pipe($x, f, g, h)=h(g(f($x)))— left to right, more readable. Both are fundamental FP patterns. Memoization is safe only for pure functions (same input always gives same output, no side effects).
📸 Verified Output:
Step 6: Recursion
💡 PHP's default stack depth handles ~100–1000 recursive calls before stack overflow. For deep recursion, use
ini_set('xdebug.max_nesting_level', 1000)or convert to iteration.array_mergeinside recursion is O(n) each call — for large arrays, use a reference-based approach.
📸 Verified Output:
Step 7: Static Functions & Built-in Callables
💡 PHP built-in functions are valid callables — you can pass
'strtolower','trim','is_int'as strings toarray_map,array_filter,usort. This works because PHP looks them up by name. The first-class callable syntaxstrtolower(...)creates aClosure— more type-safe.
📸 Verified Output:
Step 8: Complete Example — Pipeline Processor
💡 The Pipeline pattern chains transformations functionally. Each
pipe()returns a clone (immutable) so you can branch pipelines. This is how Laravel's pipeline, middleware stacks, and Guzzle request handlers work internally. Thestaticreturn type hint enables method chaining with subclasses.
📸 Verified Output:
Verification
Expected: Pipeline result: 400 (5 × 2 = 10, + 10 = 20, 20² = 400)
Summary
PHP functions are first-class citizens — typed, composable, and flexible. You've covered type declarations, named arguments, variadic functions, closures with use, arrow functions with implicit capture, higher-order functions, recursion, and a full Pipeline pattern. These skills underpin every modern PHP framework.
Further Reading
Last updated
