PHP · Beginner

Functions in PHP

10 min readUpdated September 24, 2026Every example verified

In short: A PHP function is declared with function name(parameters) { ... } and runs only when called. Parameters and return values can carry type declarations such as int or ?string, parameters may have defaults, and return hands a value back to the caller. Variables inside a function are local: it cannot see variables from the surrounding script unless they are passed in as arguments.

Why functions, and how PHP scopes them

A function gives a piece of logic a name so it can be run from several places with different inputs, tested on its own, and fixed in one place. function ticketPrice(int $age): float { ... } declares one: the parameters in parentheses receive the arguments of each call, and return sends a value back and ends the call. A function declared unconditionally at the top level of a file can be called from anywhere in that file, even above its declaration, because PHP registers all such functions before running the script.

Type declarations are optional but worth using. A parameter type such as int, float, string, bool or array is checked at call time. By default PHP is coercive: passing the string "12" to an int parameter converts it, while passing "abc" throws a TypeError. Adding declare(strict_types=1); as the first statement of the file switches calls made from that file to strict mode, where only an exact type (or int for a float parameter) is accepted. The return type after the colon is checked the same way; void declares that nothing is returned, ?int allows an int or null, and int|float (PHP 8.0) allows either.

A parameter with a default, bool $member = false, may be omitted by the caller. Since PHP 8.0 arguments can also be passed by name, ticketPrice(age: 40, base: 20.0), which lets you skip defaults in the middle and makes calls with several booleans readable. A variadic parameter, string ...$words, collects any number of remaining arguments into an array, and the same ... in a call spreads an array into separate arguments.

Scope is where PHP differs from many languages. A function body sees only its parameters and the variables it creates; it does not see the variables of the surrounding script. Reading one produces a warning and null. The right response is to pass the value in as an argument and return the result, not to reach for the global keyword, which makes functions depend on names that live elsewhere. A static variable inside a function keeps its value between calls, which is occasionally useful for counters.

Arguments are passed by value: a function receives a copy of a scalar or an array, so changing a parameter does not change the caller's variable. Declaring the parameter with &, as in array &$list, passes a reference instead, and changes flow back. Objects behave differently, as the classes lesson explains: an object variable is a handle, so a function that receives one can modify the same object.

Short functions can be written as arrow functions (PHP 7.4): fn(float $p): float => $p * 1.2. An arrow function is a value that can be stored in a variable or handed to array_map; unlike a normal function it automatically captures the outer variables it mentions, by value. The longer function () use ($rate) { ... } form does the same with an explicit list.

Syntax

 PHP · syntax
<?php
declare(strict_types=1);          // optional, must be the first statement

function name(int $required, string $optional = "x", float ...$rest): ?string
{
    // body: sees only parameters and its own variables
    return $value;                // omit return, or use : void, for none
}

$r = name(5);                     // positional
$r = name(5, optional: "y");      // named argument (PHP 8.0)
$r = name(...$args);              // spread an array into arguments

function append(array &$list, string $item): void { $list[] = $item; }   // by reference

$double = fn(int $n): int => $n * 2;             // arrow function, captures outer variables
$greet = function (string $n) use ($prefix) {    // closure with explicit capture
    return "$prefix $n";
};

Return types are written after a colon. ?type allows null; type1|type2 is a union (PHP 8.0). Parameters with defaults belong after the required ones.

Typed parameters, defaults and named arguments

A museum ticket price depends on age, membership and a base price; five calls use the parameters in different ways.

 PHP
<?php
declare(strict_types=1);

function ticketPrice(int $age, bool $member = false, float $base = 14.0): float
{
    if ($age < 6) {
        return 0.0;
    }
    $price = ($age < 18 || $age >= 65) ? $base / 2 : $base;
    if ($member) {
        $price *= 0.8;
    }
    return $price;
}

echo ticketPrice(30), "\n";
echo ticketPrice(12), "\n";
echo ticketPrice(70, true), "\n";
echo ticketPrice(age: 40, base: 20.0), "\n";
echo ticketPrice(member: true, age: 4), "\n";

Output

14
7
5.6
20
0

The first call supplies only the required $age; the defaults fill in $member and $base. The third passes two positional arguments. The fourth skips $member by naming $base, which positional arguments cannot do. The last names both arguments in any order. Early return 0.0 for young children ends the function before the other rules run. Whole-number floats print without a decimal point, so 14.0 appears as 14.

What a function can and cannot see

A script-level variable is invisible inside a function unless it is passed in; a variable created inside is invisible outside.

 PHP
<?php
$discount = 5;

function applyDiscount(int $price): int
{
    $result = $price - 5;   // $discount from the script is not visible here
    return $result;
}

function labelled(int $price, int $discount): string
{
    return "was $price, now " . ($price - $discount);
}

echo applyDiscount(40), "\n";
echo labelled(40, $discount), "\n";
echo isset($result) ? "result is visible" : "result is local", "\n";

Output

35
was 40, now 35
result is local

applyDiscount has to hard-code the 5 because $discount belongs to the script, not to the function. labelled does it properly: the discount comes in as a parameter, so the function works for any value and can be tested alone. The last line checks for $result at script level and finds nothing: it existed only while applyDiscount was running. Passing values in and returning values out is how PHP functions communicate.

Variadics, references and arrow functions

One function takes any number of words, one modifies its caller's array through a reference, and an arrow function captures a rate from the script.

 PHP
<?php
function longest(string ...$words): string
{
    $best = "";
    foreach ($words as $w) {
        if (strlen($w) > strlen($best)) {
            $best = $w;
        }
    }
    return $best;
}

function stamp(array &$list, string $note): void
{
    $list[] = $note;
}

$log = [];
stamp($log, "opened");
stamp($log, "checked");
echo count($log), " entries\n";

$rate = 1.2;
$withTax = fn(float $p): float => round($p * $rate, 2);
echo longest("fig", "pomegranate", "kiwi"), "\n";
echo $withTax(10), " ", $withTax(2.5), "\n";
echo implode(", ", array_map($withTax, [1, 5])), "\n";

Output

2 entries
pomegranate
12 3
1.2, 6

...$words gathers three strings into an array. stamp declares &$list, so the two calls append to the script's $log; without the &, each call would append to a private copy and $log would stay empty. $withTax uses $rate from the script without a use clause, which is the arrow-function convenience, and because it is a value it can be passed to array_map like any other callable.

Common mistakes

  • Expecting script variables to be visible inside a function

    Why it goes wrong: PHP functions do not see the enclosing scope. $rate used inside a function that never received it is undefined, giving a warning and null, so the calculation silently becomes 0.

    Fix: Pass the value as a parameter. Reserve global for legacy code.

     PHP · fix
    <?php
    function withTax(float $amount, float $rate): float
    {
        return $amount * (1 + $rate);
    }
    echo withTax(100, 0.2), "\n";
  • Printing the result instead of returning it

    Why it goes wrong: A function that echoes its answer returns null; $total = price(3) stores nothing useful and the value cannot be added, compared or tested.

    Fix: Return the value and let the caller decide whether to print it.

  • Passing text to a typed parameter under strict_types

    Why it goes wrong: With declare(strict_types=1), area("3", "4") throws TypeError because strings are not converted to int. Input from fgets is always a string.

    Fix: Cast at the boundary: area((int) $w, (int) $h).

  • Writing int $x = null for an optional parameter

    Why it goes wrong: This implicit nullable form is deprecated in PHP 8.4 and prints a deprecation notice.

    Fix: Declare the nullable type explicitly.

     PHP · fix
    <?php
    function find(?int $limit = null): string
    {
        return $limit === null ? "all" : "first $limit";
    }
    echo find(), " ", find(3), "\n";

Parameter forms

DeclarationAcceptsNotes
int $nan int (or a numeric string unless strict_types)required
int $n = 0an int, or nothingdefault used when omitted
?int $nan int or nullnull must be passed explicitly unless a default is given
int|float $neither typeunion type, PHP 8.0
int ...$nzero or more intscollected into an array; must be last
array &$nan array by referencechanges reach the caller

Where you use this

Split every exercise into a reading part and a solving part. A readInt() helper keeps the trim/cast noise in one place, and a solve(array $values): int function holds the logic, so you can call solve([3, 1, 2]) by hand while developing instead of retyping input. The same habit scales: real PHP applications are built from small functions and methods that each do one job, receive what they need as parameters, and return their result.

 PHP · in practice
function readInt(): int
{
    return (int) trim(fgets(STDIN));
}

function solve(array $values): int
{
    return max($values) - min($values);
}

$n = readInt();
$values = [];
for ($i = 0; $i < $n; $i++) {
    $values[] = readInt();
}
echo solve($values), "\n";

Key points

  • function name(params): type { ... } declares; the body runs only when called.
  • Parameter and return types are checked at call time; declare(strict_types=1) disables coercion.
  • Defaults make parameters optional; named arguments (PHP 8.0) can skip them.
  • ...$items collects extra arguments; ...$array spreads an array into a call.
  • Functions see only their parameters and local variables; pass values in, return values out.
  • Scalars and arrays are passed by value; &$param passes by reference.
  • fn(...) => expr is a short function that captures outer variables by value.

Try it yourself

Complete average so it returns the arithmetic mean of the values in the array. The rest of the program reads a count, then that many numbers, and prints the result.

Your program
<?php
function average(array $values): float
{
    return 0.0;
}

$n = (int) trim(fgets(STDIN));
$values = [];
for ($i = 0; $i < $n; $i++) {
    $values[] = (float) trim(fgets(STDIN));
}
echo average($values), "\n";
Input the program receives: 4 ↵ 2 ↵ 7 ↵ 5 ↵ 4
Expected output: 4.5

Practise this

Open the PHP playground

Frequently asked questions

Can I call a PHP function before it is defined in the file?

Yes, if the function is declared unconditionally at the top level of the same file: PHP reads the whole file and registers those functions before it starts executing. A function declared inside an if block or inside another function exists only once that code has run, so it must be defined before the call.

What does declare(strict_types=1) do in PHP?

It switches the file to strict type checking for function calls made from that file. Without it, PHP coerces scalar arguments: "5" passed to an int parameter becomes 5. With it, only a value of the declared type is accepted (an int is still accepted for a float parameter), and anything else throws TypeError. The declaration must be the very first statement in the file.

What is the difference between fn and function in PHP?

fn (PHP 7.4) declares an arrow function: a single expression whose value is returned, and which automatically captures any outer variables it uses, by value. function () use ($x) { ... } declares an anonymous function with a full body and an explicit capture list, and it can capture by reference with use (&$x). Named functions are declared with function name() and have no access to outer variables at all.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with PHP 8.4 at build time by the publishing checks, and the output shown is what it printed. Running PHP inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with PHP 8.4 locally.