PHPReturn Values

Return Values

The return statement is how a function hands a result back to whatever called it. As soon as PHP executes a return, the function stops running immediately — any code written after it in the same branch is dead code and will never run. Understanding exactly when and what a function returns is one of the most practical things you can learn about PHP, because it affects everything from how you structure conditionals to what var_dump() shows you when something looks like it "returned nothing."

return exits immediately

return does not just supply a value — it is also a control-flow statement. The moment it runs, PHP leaves the function, skipping every remaining line, loop iteration, or echo that would otherwise follow.

Code after return never executes

PHP
<?php
function checkAge(int $age): string
{
    if ($age < 18) {
        return "minor";
    }

    echo "This line never runs for age 15\n";
    return "adult";
}

echo checkAge(15);
echo "\n";
echo checkAge(25);
minor
adult
Guard clauses: early returns instead of deep nesting

Because return exits right away, you can use it to handle invalid or edge cases at the top of a function and return early, leaving the rest of the function to deal only with the "normal" case. This pattern is called a guard clause, and it usually reads far better than wrapping the entire function body in nested if blocks.

Guard clauses flatten nested conditionals

PHP
<?php
// Without guard clauses - nesting grows with every rule
function calculateDiscountNested(float $price, bool $isMember): float
{
    if ($price > 0) {
        if ($isMember) {
            return $price * 0.9;
        } else {
            return $price;
        }
    } else {
        return 0.0;
    }
}

// With guard clauses - each rule exits early, no nesting
function calculateDiscount(float $price, bool $isMember): float
{
    if ($price <= 0) {
        return 0.0;
    }

    if (!$isMember) {
        return $price;
    }

    return $price * 0.9;
}

echo calculateDiscount(100.0, true);
echo "\n";
echo calculateDiscount(100.0, false);
echo "\n";
echo calculateDiscount(-5.0, true);
90
100
0

Both versions of calculateDiscount() compute the same thing, but the guard-clause version keeps the "happy path" — the discount calculation — at the lowest indentation level, and pushes the exceptional cases (invalid price, non-member) to the top where they are handled and immediately forgotten about.

Returning multiple values with an array

PHP functions can only return a single value directly, but that value is very often an array, which lets you bundle several results together. On the caller's side, list destructuring with [$a, $b] = someFunction() (or the older list($a, $b) = someFunction() syntax) unpacks that array straight into separate variables.

Returning min and max together

PHP
<?php
function minMax(array $numbers): array
{
    return [min($numbers), max($numbers)];
}

[$low, $high] = minMax([4, 19, 2, 87, 33]);

echo "Low: {$low}, High: {$high}";
Low: 2, High: 87

You can also destructure an associative array by matching keys, which is often clearer than relying on positional order because the caller does not need to remember which slot holds which value.

Destructuring by key

PHP
<?php
function parseName(string $fullName): array
{
    [$first, $last] = explode(" ", $fullName, 2);
    return ["first" => $first, "last" => $last];
}

["first" => $firstName, "last" => $lastName] = parseName("Grace Hopper");

echo "{$firstName} / {$lastName}";
Grace / Hopper
The old list() syntax

Before the short [$a, $b] = ... syntax existed, PHP used list($a, $b) = ... for the same purpose. Both forms are still valid today and behave identically; list() simply predates the square-bracket shorthand and shows up often in older codebases.

list() is equivalent to the square-bracket form

PHP
<?php
function divide(int $a, int $b): array
{
    return [intdiv($a, $b), $a % $b];
}

list($quotient, $remainder) = divide(17, 5);

echo "{$quotient} remainder {$remainder}";
3 remainder 2
No return statement means null

If a function's execution path never reaches a return statement — either because there simply isn't one, or because none of the conditional branches that contain one were taken — the function implicitly returns null. This is easy to overlook because nothing errors; the caller just silently gets null back.

A function with no return statement

PHP
<?php
function logAndForget(string $message)
{
    echo "LOG: {$message}\n";
    // no return statement here
}

$result = logAndForget("Something happened");
var_dump($result);
LOG: Something happened
NULL
A forgotten branch silently returns null
If a function has multiple `if` branches and only some of them contain a `return`, forgetting one is a common source of bugs: the function will still "work" without any error, but callers of that missed branch quietly receive `null` instead of a real value. Always make sure every code path in a function that is supposed to produce a value actually returns one, including a final fallback `return` after the last `if`.
  • return; with no value is equivalent to return null; — both exit the function and hand back null.

  • A void return type declaration documents that a function is not meant to return a meaningful value at all (covered on the Type Declarations page).

  • Destructuring assignment ([$a, $b] = ...) silently assigns null to any variable whose position or key is missing from the returned array — it will not throw an error.

  • return inside a loop exits the entire function, not just the loop; use break or continue if you only want to affect the loop.

Destructuring and missing keys
If `parseName()` from the example above returned only `["first" => "Grace"]` and you tried `["first" => $f, "last" => $l] = parseName(...)`, `$l` would simply become `null` rather than raising an error. It is worth pairing destructuring with `isset()` or a default value when a key's presence is not guaranteed.
Tip
When a function conceptually needs to return more than two or three related values, consider returning an associative array (or a small class/DTO) instead of a long positional array. Key-based destructuring at the call site is far more readable than counting array positions, and it keeps working correctly even if you later add another field.