PHP Functions
A function is a named, reusable block of code that performs a specific task. Instead of repeating the same logic every time you need it, you define it once and call it by name whenever it’s needed. Functions are the basic unit of organization in PHP: they let you break a large script into small, testable pieces, hide implementation details behind a clear interface, and avoid duplicating logic across your codebase.
Overview / How Functions Work
PHP has two broad categories of functions: built-in functions (like strlen(), array_map(), or count()) that ship with the language, and user-defined functions that you write yourself with the function keyword. Both are called the same way: functionName(arguments).
Internally, when the Zend Engine compiles a PHP script, every function declaration (outside of a conditional block) is registered in a global function table before the script actually executes line by line. This is why you can call a function in your code before its definition appears further down the file — PHP already knows about it from the compilation pass. Functions declared conditionally (inside an if, for example) are only registered once that branch actually runs.
Each time you call a function, PHP pushes a new stack frame onto the call stack. That frame has its own local variable scope — variables created inside a function do not exist outside it, and variables from the calling code are not visible inside the function unless you explicitly pass them in as arguments (or use global, which we’ll cover below). When the function finishes — either by hitting a return statement or reaching its closing brace — its stack frame is destroyed and control returns to the caller with whatever value was returned (or null if nothing was returned).
PHP functions are also values in a sense: you can store a reference to a function in a variable (a “callable”), pass functions as arguments to other functions, and return functions from functions. This is what powers closures, callbacks used by array functions like array_map(), and modern arrow functions.
Syntax
function functionName(type $param1, type $param2 = defaultValue): returnType {
// function body
return $value;
}
function— the keyword that begins a function declaration.functionName— a valid identifier; by convention, PHP built-ins usesnake_caseand many projects usecamelCasefor user-defined functions.- Parameters — a comma-separated list inside parentheses. Each can have an optional type declaration (
int,string,array, a class name, a union type likeint|string, etc.) and an optional default value, which makes the parameter optional. - Return type — written after a colon following the closing parenthesis. Use
voidif the function returns nothing, or a nullable type like?stringif it may returnnull. return— exits the function immediately and hands a value back to the caller. A function without an explicitreturnimplicitly returnsnull.
Examples
Example 1: A simple typed function
<?php
function calculateArea(float $width, float $height): float
{
return $width * $height;
}
$area = calculateArea(4.5, 3.2);
echo "Area: " . $area . "\n";
$area2 = calculateArea(width: 10, height: 2);
echo "Area 2: " . $area2 . "\n";
Output:
Area: 14.4
Area 2: 20
The first call passes arguments positionally. The second call uses PHP’s named arguments feature (available since PHP 8.0), which lets you pass values by matching them to parameter names instead of position — useful for readability, especially with functions that take several optional parameters.
Example 2: Default parameters and variadic functions
<?php
function greet(string $name, string $greeting = "Hello"): string
{
return "{$greeting}, {$name}!";
}
function sum(int ...$numbers): int
{
$total = 0;
foreach ($numbers as $number) {
$total += $number;
}
return $total;
}
echo greet("Maria") . "\n";
echo greet("Sam", "Welcome") . "\n";
echo sum(1, 2, 3, 4, 5) . "\n";
Output:
Hello, Maria!
Welcome, Sam!
15
greet() shows a default parameter: if $greeting is omitted, "Hello" is used automatically. sum() uses the variadic ...$numbers syntax, which collects any number of arguments into an array inside the function — you can call it with 1, 5, or 50 numbers and it just works.
Example 3: Arrow functions, closures, and first-class callables
<?php
$numbers = [1, 2, 3, 4, 5, 6];
$doubled = array_map(fn(int $n): int => $n * 2, $numbers);
echo implode(", ", $doubled) . "\n";
$threshold = 10;
$aboveThreshold = array_filter($doubled, function (int $n) use ($threshold): bool {
return $n > $threshold;
});
echo implode(", ", $aboveThreshold) . "\n";
function square(int $n): int
{
return $n * $n;
}
$squareFn = square(...);
echo $squareFn(7) . "\n";
Output:
2, 4, 6, 8, 10, 12
12
49
The fn(int $n): int => $n * 2 syntax is an arrow function — a compact closure that automatically captures variables from the surrounding scope by value, without needing use. The regular closure passed to array_filter() needs an explicit use ($threshold) clause to import $threshold from the outer scope. Finally, square(...) is PHP 8.1’s first-class callable syntax, which turns any named function into a callable value you can store and invoke later.
Under the Hood: Scope and State
<?php
$counter = 0;
function increment(): void
{
global $counter;
$counter++;
}
increment();
increment();
echo "Counter: {$counter}\n";
function makeCounter(): callable
{
$count = 0;
return function () use (&$count) {
$count++;
return $count;
};
}
$counter2 = makeCounter();
echo $counter2() . "\n";
echo $counter2() . "\n";
Output:
Counter: 2
1
2
Every function gets a fresh, isolated local scope — a variable named $counter inside increment() would normally be a completely different variable from the $counter outside it. The global keyword bridges that gap by binding the local name to the actual global variable, which is powerful but easy to misuse (see Common Mistakes below). The second half shows a closure capturing $count by reference with use (&$count): each call to makeCounter() creates a brand-new, private $count that persists between calls to the returned closure, giving you private state without a class.
Common Mistakes
Mistake 1: Echoing instead of returning
Beginners often print a result inside a function instead of returning it, then try to use the “result” of the call — which is actually null.
<?php
function calculateTotal(array $prices)
{
$total = 0;
foreach ($prices as $price) {
$total += $price;
}
echo $total;
}
$total = calculateTotal([10, 20, 30]);
echo "Total: " . $total;
This runs without error but prints 60Total: — the function echoes 60 immediately, and $total in the caller ends up null because nothing was returned. The fix is to return the value and let the caller decide what to do with it:
<?php
function calculateTotal(array $prices): float
{
$total = 0;
foreach ($prices as $price) {
$total += $price;
}
return $total;
}
$total = calculateTotal([10, 20, 30]);
echo "Total: " . $total;
Output: Total: 60
Mistake 2: Expecting arrays to change without reassigning or passing by reference
PHP arrays are passed to functions by value by default — the function gets its own copy. Modifying the local copy inside the function does not change the caller’s variable.
<?php
function addTax(array $prices, float $rate): void
{
foreach ($prices as $key => $price) {
$prices[$key] = $price + ($price * $rate);
}
}
$prices = [100, 200, 300];
addTax($prices, 0.08);
echo implode(", ", $prices);
This prints the untouched 100, 200, 300 because $prices inside addTax() is a separate copy. Fix it by returning the modified array and reassigning it (the idiomatic approach), or by declaring the parameter as a reference with &$prices:
<?php
function addTax(array $prices, float $rate): array
{
foreach ($prices as $key => $price) {
$prices[$key] = $price + ($price * $rate);
}
return $prices;
}
$prices = [100, 200, 300];
$prices = addTax($prices, 0.08);
echo implode(", ", $prices);
Output: 108, 216, 324
Best Practices
- Always declare parameter and return types — they catch bugs early and make the function’s contract obvious at a glance.
- Prefer returning values over echoing inside a function; let the caller decide how to display or use the result.
- Keep functions small and focused on a single task — if you struggle to name a function without using “and”, split it into two.
- Avoid the
globalkeyword; pass values as parameters and return results instead, which keeps functions predictable and easier to test. - Use default parameter values instead of checking
func_num_args()or overloading logic with conditionals. - Reach for arrow functions (
fn() =>) for short, single-expression callbacks, and regular closures when you need a multi-line body or reference capture. - Name functions with verbs that describe the action (
calculateTotal,isValidEmail) so calls read like plain sentences.
Practice Exercises
- Write a function
isPalindrome(string $text): boolthat returnstrueif a string reads the same forwards and backwards (ignore case). Test it with"Racecar"and"Hello". - Write a variadic function
average(float ...$numbers): floatthat returns the average of any number of arguments passed to it. - Write a function
makeMultiplier(int $factor): callablethat returns a closure. Calling the returned closure with a number should multiply it by$factor. For example,$triple = makeMultiplier(3); $triple(5);should produce15.
Summary
- A function is a named, reusable block of code defined with the
functionkeyword and invoked withfunctionName(). - Function declarations are registered during compilation, so top-level functions can be called before their definition appears in the file.
- Each function call gets its own isolated local scope; use parameters and
returnto move data in and out rather than theglobalkeyword. - Parameters can have type declarations, default values, and be variadic (
...$args); PHP 8 also supports named arguments. - Arrow functions (
fn() =>) and closures (function () use (...)) let you treat functions as values — pass them around, store them in variables, and capture outer variables by value or by reference. - Arrays and objects passed to functions do not mutate the caller’s variable unless passed by reference (
&$param) or the modified copy is returned and reassigned.
