PHP Language Basics

Functions

A function is a named unit of behavior. It accepts input, performs one defined job, and can return a result to the code that called it.

Functions are not only a way to avoid duplicated lines. A useful function gives a rule a name and a boundary. calculateTaxCents() communicates more than the multiplication inside it, and formatCustomerName() tells a reader why trimming and joining text are happening.

Use a function when a piece of logic has a clear purpose, is called more than once, needs focused testing, or would make the surrounding script easier to read.

Declare And Call A Function

A named function starts with function, followed by its name, parameter list, optional return type, and body:

PHP example
<?php

function formatFullName(string $firstName, string $lastName): string
{
    $cleanFirstName = trim($firstName);
    $cleanLastName = trim($lastName);

    return "$cleanFirstName $cleanLastName";
}

$fullName = formatFullName(' Ada ', ' Lovelace ');

echo $fullName, PHP_EOL;

Output:

Ada Lovelace

Declaring the function defines the behavior. Calling formatFullName(...) runs it. Parentheses are required for the call, even when a function has no parameters.

Execution enters the function with values assigned to its parameters. When PHP reaches return, execution leaves the function and the returned value replaces the call expression. In the example, the returned string becomes the value assigned to $fullName.

Parameters And Arguments Are Different

Parameters are the input names in the declaration:

function formatFullName(string $firstName, string $lastName): string

Arguments are the values supplied by a particular call:

PHP example
formatFullName('Ada', 'Lovelace');

Here, $firstName and $lastName are parameters. 'Ada' and 'Lovelace' are arguments.

That distinction matters when debugging. "The $lastName parameter expects a string" describes the function contract. "The second argument was 'Lovelace'" describes one call.

By default, ordinary arguments are passed by value. A function works with its parameter value without changing the caller's variable:

PHP example
<?php

function normaliseCode(string $code): string
{
    $code = strtoupper(trim($code));

    return $code;
}

$code = ' ab-12 ';
$normalisedCode = normaliseCode($code);

var_dump($code);
var_dump($normalisedCode);

Output:

string(7) " ab-12 "
string(5) "AB-12"

PHP also supports pass-by-reference parameters, but they create an intentional mutation of caller-owned data. They are deferred until references are covered explicitly. Returning a transformed value is clearer for most beginner functions.

A Signature Describes The Contract

This signature says the function requires two strings and promises a string result:

function formatFullName(string $firstName, string $lastName): string

Parameter names should describe meaning, not merely type. $subtotalCents is better than $number because it records both purpose and unit. $ratePercent is clearer than $rate when callers must pass 20 rather than 0.20.

Basic parameter and return declarations make examples easier to understand and help PHP and development tools detect mismatched values. A later lesson covers coercion, nullable types, void, unions, and strict_types in detail. For now, make simple function contracts visible.

Return A Value Instead Of Printing It

A calculation or formatter should usually return its result:

PHP example
<?php

function formatPrice(int $cents): string
{
    return 'GBP ' . number_format($cents / 100, 2);
}

$basketLabel = formatPrice(1299);
$emailLabel = formatPrice(1299);

echo "Basket: $basketLabel\n";
echo "Email: $emailLabel\n";

formatPrice() decides how a price looks. It does not decide whether that text belongs in a command-line message, an HTML page, an email, or a test assertion.

A function that echoes directly combines calculation with output. Sometimes output is the function's actual job, but business rules and formatting functions are more reusable when the caller controls the destination.

Do not confuse return with echo:

  • return sends a value to the caller and leaves the function;
  • echo writes output and then execution continues;
  • assigning a function call stores its returned value;
  • ignoring a return value discards it.

return Ends The Current Function Call

Statements after an executed return are unreachable:

PHP example
<?php

function shippingMessage(bool $isPaid): string
{
    if (!$isPaid) {
        return 'Waiting for payment';
    }

    return 'Ready to ship';
}

echo shippingMessage(false), PHP_EOL;

The first return handles the unpaid path immediately. If $isPaid is true, that branch is skipped and execution reaches the second return.

This early-return pattern is useful when one condition prevents the main result. Each possible path must still return the type promised by the signature. A function declared : string should not accidentally reach its closing brace without returning a string.

Exceptions provide another way for a function to stop when it cannot complete its contract, but throwing and catching them belongs to the later errors and exceptions lesson.

Use Default Values For Genuinely Optional Input

A default value lets a caller omit an optional argument:

PHP example
<?php

function buildGreeting(string $name, string $prefix = 'Hello'): string
{
    return "$prefix, $name";
}

echo buildGreeting('Mina'), PHP_EOL;
echo buildGreeting('Mina', 'Welcome back'), PHP_EOL;

Output:

Hello, Mina
Welcome back, Mina

The default is used only when the argument is omitted. Passing another value replaces it. Passing null does not mean "use the default" and would not satisfy this parameter's string declaration.

Put required parameters before optional parameters:

function buildGreeting(string $name, string $prefix = 'Hello'): string

Putting a required parameter after one with a default prevents that earlier argument from being omitted positionally. In common forms, PHP treats the supposedly optional parameter as required and deprecates the declaration. Keep signatures ordered from required inputs to optional choices.

Defaults should represent unsurprising convenience, not a hidden business decision. A default greeting prefix is visible and low risk. A silently selected tax jurisdiction or permission level would deserve an explicit argument.

Positional And Named Arguments

The calls so far use argument position:

PHP example
buildGreeting('Mina', 'Welcome back');

PHP 8 and later also support named arguments:

PHP example
buildGreeting(name: 'Mina', prefix: 'Welcome back');

Named arguments can make calls with several similar values easier to read, and they can skip optional parameters. Positional arguments must come before named arguments when both styles are combined.

Use named arguments deliberately. Parameter names become part of how callers invoke the function, so renaming a public parameter can break named calls even when the value types and order stay the same. For short project-local calls, positional arguments are often sufficient.

Compose Small Functions

One function can use another function's returned value:

PHP example
<?php

function calculateTaxCents(int $subtotalCents, int $ratePercent): int
{
    return (int) round($subtotalCents * $ratePercent / 100);
}

function calculateTotalCents(int $subtotalCents, int $ratePercent): int
{
    $taxCents = calculateTaxCents($subtotalCents, $ratePercent);

    return $subtotalCents + $taxCents;
}

echo calculateTotalCents(2500, 20), PHP_EOL;

Output:

3000

calculateTotalCents() delegates one named rule rather than repeating the tax formula. This is useful when the extracted function represents a real concept and can be tested independently.

Do not split every operator into a separate function. Extraction should improve the vocabulary or remove meaningful duplication, not force a reader to jump through many files to understand one line.

Keep One Clear Responsibility

A focused function should be explainable with one short sentence:

  • formatFullName() cleans and joins two name parts;
  • calculateTaxCents() calculates tax in cents;
  • isPaidOrder() answers one yes-or-no question.

A function that validates a form, saves a database row, sends an email, and prints HTML has several reasons to change. It is difficult to reuse and difficult to verify without triggering all of its effects.

At this stage, prefer functions that calculate, classify, or format and then return a result. These are predictable: the same arguments produce the same result, and the caller can inspect the return value directly.

Name Functions For Their Result Or Action

Use a verb that reveals the job:

  • formatPrice() returns formatted text;
  • calculateTotalCents() returns a calculation;
  • isEligibleForDiscount() returns a boolean decision;
  • printReceipt() would perform output as its named action.

Avoid vague names such as process(), handleData(), doThing(), or runLogic(). They hide the rule a reader is trying to find.

Call functions with the same spelling and casing used in their declarations. PHP's ASCII function names are case-insensitive, but relying on that behavior makes searches and reviews less reliable. Also avoid declaring a name already used by another function in the same namespace; PHP cannot redeclare a function.

Test Boundaries, Not Only One Happy Example

A function call is easy to repeat with controlled values. For a shipping threshold function, useful checks include an amount below the threshold, exactly at it, and above it. For a formatter, try surrounding whitespace and already-clean input.

Write the expected result before running the code:

Call Expected result
calculateTaxCents(2500, 20) 500
calculateTaxCents(1999, 20) 400 after rounding
calculateTaxCents(2500, 0) 0

One passing example proves only one path. Boundary checks expose incorrect comparison operators, percentage conventions, rounding assumptions, and missing return paths.

Common Function Mistakes

Symptom Likely cause
Function prints when a value is needed Used echo instead of return
Missing result or TypeError Function reached the end without returning the promised value
Later statements never run An earlier return already left the function
Arguments affect the wrong inputs Positional order does not match the parameter order
Optional argument cannot be omitted Optional parameter was placed before a required one
Call is hard to understand Function or parameter names do not describe purpose and units
Small change requires editing copies Repeated rule has not been given one function
Function is difficult to test It combines calculation with unrelated side effects
Named call breaks after refactoring A parameter name used by callers was changed

What You Should Be Able To Do

After this lesson, you should be able to declare and call a named function, distinguish parameters from arguments, read a basic function signature as a contract, return a value to the caller, explain why return ends the current call, use an early return for a simple branch, put required parameters before defaults, recognise positional and named calls, compose small functions, choose names that reveal purpose and units, and test more than one input path.

The next lesson examines variable scope, globals, static variables, closures, and captured values. Error handling and detailed type behavior are covered separately so each boundary is introduced with enough context.

Official references: PHP's manual pages for user-defined functions, function parameters and arguments, and returning values.

Practice

Task: Format Full Name

Task

Write a function named formatFullName() with this signature:

function formatFullName(string $firstName, string $lastName): string

The function should:

  • trim whitespace from both name arguments;
  • join the cleaned values with exactly one space;
  • return the full name;
  • contain no echo statement.

Call it with both sets of arguments below and print each returned value outside the function:

PHP example
formatFullName(' Ada ', ' Lovelace ');
formatFullName('Grace', 'Hopper');

The exact output should be:

Ada Lovelace
Grace Hopper

Hints

  • Store the result of each trim() call in a clearly named local variable.
  • Build the final string after both values have been cleaned.
  • The caller can pass the function call directly to echo, or assign the return value first.
Show solution

Solution

PHP example
<?php

function formatFullName(string $firstName, string $lastName): string
{
    $cleanFirstName = trim($firstName);
    $cleanLastName = trim($lastName);

    return "$cleanFirstName $cleanLastName";
}

$firstFullName = formatFullName(' Ada ', ' Lovelace ');
$secondFullName = formatFullName('Grace', 'Hopper');

echo $firstFullName, PHP_EOL;
echo $secondFullName, PHP_EOL;

// Prints:
// Ada Lovelace
// Grace Hopper

Explanation

The declaration names two string parameters and promises a string result. Each call supplies different arguments, but both calls run the same cleaning and joining rule.

formatFullName() returns its result instead of choosing where to display it. The caller stores each returned string and prints it. The same function could therefore be used by a template, an email builder, or a test without capturing output from inside the function.

The second call is useful even though its arguments have no surrounding spaces. It confirms that the formatter also preserves already-clean input and does not depend on whitespace being present.

Task: Predict Default And Named Arguments

Task

Before running this code, predict exactly what each line prints:

PHP example
<?php

function buildGreeting(string $name, string $prefix = 'Hello'): string
{
    return "$prefix, $name";
}

echo buildGreeting('Mina'), PHP_EOL;
echo buildGreeting('Mina', 'Welcome back'), PHP_EOL;
echo buildGreeting(prefix: 'Hi', name: 'Omar'), PHP_EOL;

Write down:

  1. which call uses the default value;
  2. which call replaces the default positionally;
  3. how PHP assigns the arguments in the final call;
  4. the three output lines in their exact order.

Then run the unchanged code and compare the output with your prediction.

Hints

  • A default is used only when its argument is omitted.
  • In the second call, argument position maps 'Mina' to $name and 'Welcome back' to $prefix.
  • Named arguments are matched by parameter name, not by the order in which they are written.
Show solution

Solution

PHP example
<?php

function buildGreeting(string $name, string $prefix = 'Hello'): string
{
    return "$prefix, $name";
}

echo buildGreeting('Mina'), PHP_EOL;
echo buildGreeting('Mina', 'Welcome back'), PHP_EOL;
echo buildGreeting(prefix: 'Hi', name: 'Omar'), PHP_EOL;

// Prints:
// Hello, Mina
// Welcome back, Mina
// Hi, Omar

Explanation

The first call supplies only the required $name argument, so PHP assigns the declared default Hello to $prefix.

The second call is positional. The first argument goes to $name, and the second goes to $prefix, replacing the default with Welcome back.

The final call names both target parameters. Although prefix appears before name in the call, PHP assigns 'Hi' to $prefix and 'Omar' to $name. Named argument order does not change the function's parameter meanings.

All three calls receive a returned string. The surrounding echo statements, not buildGreeting(), decide when those strings are written to output.

Task: Write Tax Function

Task

Write a function named calculateTaxCents() with this signature:

function calculateTaxCents(int $subtotalCents, int $ratePercent = 20): int

For this exercise, assume the caller has already validated that both arguments are non-negative.

The function should:

  • return 0 immediately when the subtotal or rate is 0;
  • otherwise multiply the subtotal by the percentage and divide by 100;
  • round the result with round();
  • cast the rounded result to int and return it;
  • contain no output statements.

Call it with these arguments:

PHP example
calculateTaxCents(2500);
calculateTaxCents(1999);
calculateTaxCents(2500, 0);

Print each returned value. The exact output should be:

500
400
0

Hints

  • The first two calls omit $ratePercent, so they use the default 20.
  • Use || in the early-return condition.
  • 1999 * 20 / 100 is 399.8, which rounds to 400.
  • Invalid input policies and exceptions are covered in a later lesson.
Show solution

Solution

PHP example
<?php

function calculateTaxCents(int $subtotalCents, int $ratePercent = 20): int
{
    if ($subtotalCents === 0 || $ratePercent === 0) {
        return 0;
    }

    return (int) round($subtotalCents * $ratePercent / 100);
}

echo calculateTaxCents(2500), PHP_EOL;
echo calculateTaxCents(1999), PHP_EOL;
echo calculateTaxCents(2500, 0), PHP_EOL;

// Prints:
// 500
// 400
// 0

Explanation

The signature records the unit convention: the subtotal is in cents and the rate is a whole percentage. A caller passes 20 for 20 percent, not 0.20. The default makes 20 percent available when the second argument is omitted.

The first branch is an early return. If either factor is zero, the tax must be zero, so the function can finish without evaluating the remaining formula.

The normal path calculates the percentage, rounds fractional cents, casts the result to the promised integer type, and returns it. The 1999 case checks the rounding boundary rather than only using a subtotal that produces an exact whole-cent result.

This function assumes validated non-negative input because the exercise is about function calls and return paths. The later errors and exceptions lesson defines how a function should reject values that violate its contract.