PHP functions define callable behavior with local scope, explicit parameters, named and variadic arguments, return contracts, closures, arrow functions, references, and multiple callable forms.
Strong function design makes inputs, mutation, timing, errors, and effects visible, maps external dispatch through allowlists, and tests boundaries without depending on mutable global state.
<?php
function lineTotal(float $price, int $quantity = 1): float
{
return $price * $quantity;
}
echo lineTotal(125.50, 2);
251
The caller receives a number and decides whether to print, format, store, or compare it.
A pure function returns the same result for the same input and does not modify outside state. Pure functions are straightforward to test and combine.
<?php
function slugify(string $title): string
{
$normalized = strtolower(trim($title));
$replaced = preg_replace('/[^a-z0-9]+/', '-', $normalized);
return trim($replaced ?? '', '-');
}
echo slugify(' PHP Function Guide ');
php-function-guide
A closure is a function value passed to another function. Traditional anonymous functions import outer variables with use. Arrow functions automatically capture referenced outer values by value.
Use a named function for behavior reused across the application and a short closure for behavior local to one operation.
<?php
$taxRate = 0.18;
$prices = [100, 250];
$withTax = array_map(
fn (float $price): float => $price * (1 + $taxRate),
$prices
);
echo implode(', ', $withTax);
118, 295
A variadic parameter collects remaining arguments into an array and must be final. The spread operator unpacks an array into arguments.
<?php
function totalScores(int ...$scores): int
{
return array_sum($scores);
}
$values = [8, 9, 10];
echo totalScores(...$values);
27
| Behavior | Preferred design |
|---|---|
| Calculate a total | Return the number |
| Format a label | Return the string |
| Write a file | Name the I/O action and report failure |
| Send a response | Keep framework output at the controller boundary |
A user-defined function creates a reusable callable with parameters, a body, and an optional return contract. Function names normally live in a namespace and are case-insensitive when called, but code should preserve declared case. Use names that describe an outcome or action rather than implementation mechanics.
Functions have their own local variable scope. Variables from the outer file are not automatically available inside a named function. Pass dependencies and data as parameters; broad `global` access hides inputs, creates test coupling, and makes one request affect another in long-running processes.
A function declaration can be conditionally defined, but the definition occurs only when execution reaches that branch. Avoid conditional global function definitions in application code because call availability then depends on runtime order. Classes or explicit callable registration provide clearer extension points.
PHP does not support overloading user-defined functions by signature. A second function with the same resolved name causes a redeclaration error. Model variants through optional parameters, value objects, distinct names, or polymorphic collaborators according to the domain.
Parameters are declared by the function; arguments are supplied by the caller. Required parameters should precede optional parameters, and defaults must be valid constant expressions under the supported PHP version. Use defaults only when they represent a real domain default, not to conceal missing required input.
Named arguments bind by parameter name, can skip optional parameters, and make some calls clearer. They also make public parameter names part of the compatibility surface. Do not rename parameters casually in public functions, interfaces, or inherited methods when callers may use named arguments.
A variadic parameter gathers remaining arguments into an array and must be last. Argument unpacking expands arrays or Traversable values under supported rules. Prefer an iterable or options object when a call can accept an unbounded collection or many independent switches.
Arguments are passed by value by default using PHP copy-on-write semantics for arrays and strings; objects use object handles. An ampersand parameter allows the function to rebind the caller variable. Reserve by-reference parameters for APIs whose mutation is unmistakable, and never add `&` as a performance superstition.
A function without an explicit return statement returns null. Every branch should honor one documented contract so callers do not need to guess whether false, null, an empty array, an object, or an exception signals failure. Use a value object or enum-backed result when several outcomes need data.
Return type declarations enforce the returned value under PHP type rules. Union and nullable types can represent legitimate alternatives, while `never` states that normal control never returns. Avoid `mixed` when a narrower contract is knowable; it pushes validation into every caller.
Returning by reference is a specialized feature and requires both a reference-return declaration and compatible caller behavior. It exposes internal storage to mutation and is rarely appropriate for application APIs. Return values or dedicated mutation methods instead.
Generators return a Generator object immediately and use `yield` to produce values lazily; they do not follow an ordinary one-value return flow. Keep generator, iterable, and scalar-return contracts distinct in names, types, documentation, and tests.
An anonymous function is a Closure object and can capture outer variables through `use`. Capture by value takes the value at closure creation under PHP semantics; capture by reference observes and can change the outer variable. Make reference capture visible because it creates shared mutable state.
Arrow functions use concise `fn` syntax, contain one expression, and automatically capture referenced outer variables by value. They are excellent for short transformations and predicates, but a named closure is clearer when behavior needs statements, error handling, or explanation.
A closure declared `static` is not bound to an object through `$this`. Use static closures for callbacks that do not need instance state; this documents independence and avoids accidental object retention in long-lived callback registries.
Closures can retain large objects or service containers beyond the intended request or job lifetime. Capture only required values, unregister stored callbacks, and be careful in workers where process memory survives many jobs.
PHP callables may be closures, function-name strings, method arrays, invokable objects, or first-class callable objects. Prefer first-class callable syntax or explicit closures when refactoring support and static analysis matter. Validate configured callables before registering them.
Variable functions call the name stored in a variable, but language constructs are not ordinary functions and cannot all be invoked this way. Never dispatch directly from a request parameter. Map external action names to an allowlisted callable registry and authorize the action separately.
An invokable object implements `__invoke` and can carry typed dependencies and configuration. It works well for command handlers, validators, and middleware where a bare closure would hide construction and test setup. Keep invocation focused on one responsibility.
Callbacks should document timing, repeatability, argument shape, return use, exception behavior, and ownership. A caller needs to know whether a callback runs immediately, later, once, per item, or until it returns a stop signal.
Separate pure calculations from I/O, time, randomness, environment, and global state. Pass effectful collaborators behind narrow interfaces or callables. A pure core with a small imperative shell is easier to test and reuse than a function that reads every dependency internally.
Validate input at the boundary that understands it, then keep internal functions typed around valid values. Do not repeat the same defensive checks in every helper or accept raw request arrays deep inside the domain. Convert to named values once.
Test normal input, each boundary, default and named calls, invalid types, thrown dependencies, reference mutation where intentional, closure capture timing, and callback failure. Verify side effects and output, not private helper call counts alone.
Profile before optimizing function structure. Database round trips, filesystem access, network calls, repeated parsing, and poor algorithms dominate ordinary call overhead. Keep OpCache and production configuration in mind, but preserve readable contracts first.
0 of 2 checked
Try this next
0 of 2 completed
Explore 500+ free tutorials across 20+ languages and frameworks.