Default values
A parameter can have a default value, which is used when the caller leaves that argument out. That makes the parameter optional:
<?php
function priceWithTax($price, $rate = 0.18)
{
return round($price * (1 + $rate), 2);
}
echo priceWithTax(100) . "\n";
echo priceWithTax(100, 0.05) . "\n";118
105Optional parameters must come after the required ones. A default must be a constant expression, such as a number, a string, an array, null or a constant, not a function call.
Named arguments
Since PHP 8 you can pass arguments by name instead of by position. Named arguments make calls easier to read, and they let you skip optional parameters in the middle:
<?php
function formatPrice($amount, $currency = "INR", $decimals = 2, $symbolFirst = true)
{
$symbols = ["INR" => "₹", "USD" => "$", "GBP" => "£"];
$number = number_format($amount, $decimals);
$symbol = $symbols[$currency] ?? $currency . " ";
return $symbolFirst ? $symbol . $number : $number . " " . $symbol;
}
echo formatPrice(1499) . "\n";
echo formatPrice(1499, decimals: 0) . "\n";
echo formatPrice(amount: 24.5, currency: "GBP") . "\n";
echo formatPrice(24.5, symbolFirst: false, currency: "USD") . "\n";₹1,499.00
₹1,499
£24.50
24.50 $The second call sets only decimals without repeating the currency. Named arguments can be in any order, and you can mix them with positional ones as long as the positional ones come first. PHP's built-in functions support them too: str_pad(string: "7", length: 3, pad_string: "0", pad_type: STR_PAD_LEFT) is clearer than four bare values.
Parameter types
By default a parameter accepts anything. Adding a type declaration in front of the name tells PHP what is allowed, and PHP checks it on every call:
<?php
function lineTotal(float $price, int $quantity): float
{
return $price * $quantity;
}
echo lineTotal(18.75, 4) . "\n";
echo lineTotal(10, 3) . "\n";75
30The integer 10 was accepted for the float parameter, because PHP widens integers to floats. In the default "coercive" mode, PHP also converts compatible scalars such as the string "4" to an int. Values that cannot be converted throw a TypeError:
<?php
function lineTotal(float $price, int $quantity): float
{
return $price * $quantity;
}
echo lineTotal(18.75, "four");Fatal error: Uncaught TypeError: lineTotal(): Argument #2 ($quantity) must be of type int, string given, called in /path/to/script.php on line 7 and defined in /path/to/script.php:2
Stack trace:
#0 /path/to/script.php(7): lineTotal(18.75, 'four')
#1 {main}
thrown in /path/to/script.php on line 2With declare(strict_types=1); at the top of the file, even "4" is rejected, as shown in Comparisons, == versus === and type juggling.
The types you will use most:
| Type | Accepts |
|---|---|
int, float, string, bool |
That scalar type |
array |
Any array |
?string |
A string or null (any type can be made nullable with ?) |
int|float |
Either type (a union type) |
mixed |
Anything |
callable |
A function that can be called |
A class name, such as DateTime |
An object of that class |
Return types
A return type after the brackets, ): float, declares what the function gives back. PHP checks it when the function returns. void means the function returns nothing, and never means it never returns at all (it always throws or exits):
<?php
function logMessage(string $message): void
{
echo "[log] $message\n";
}
function findDiscount(string $code): ?int
{
$codes = ["SAVE10" => 10, "SAVE25" => 25];
return $codes[$code] ?? null;
}
logMessage("Checking coupons");
var_dump(findDiscount("SAVE25"));
var_dump(findDiscount("FREE"));[log] Checking coupons
int(25)
NULLTypes do more than catch mistakes. They document the function: a reader knows from the first line that findDiscount() takes a string and returns an integer or null, and editors use them to warn you as you type.
Any number of arguments
A parameter written with three dots, ...$name, is variadic: it collects all remaining arguments into an array. It must be the last parameter:
<?php
function total(float ...$prices): float
{
return array_sum($prices);
}
echo total(89.5, 18.75, 12) . "\n";
echo total() . "\n";120.25
0The same three dots do the opposite at the call site: they spread an array into separate arguments.
<?php
function total(float ...$prices): float
{
return array_sum($prices);
}
$cart = [24.99, 14.99, 9.5];
echo total(...$cart) . "\n";
echo max(...$cart) . "\n";49.48
24.99Passing by reference
Arguments are normally passed by value: the function gets a copy, and changes inside it do not affect the caller's variable. An & before the parameter passes the variable itself, so the function can change it:
<?php
function addItem(array &$cart, string $item): void
{
$cart[] = $item;
}
$cart = ["Mug"];
addItem($cart, "Keyboard");
print_r($cart);Array
(
[0] => Mug
[1] => Keyboard
)Some built-in functions, such as sort(), work this way. In your own code, returning a new value is usually clearer than changing the argument, so use references sparingly.
Common mistakes
- Putting an optional parameter before a required one. PHP 8 deprecates it, and it makes positional calls confusing.
- Misspelling a named argument, which is an error: the name must match the parameter exactly.
- Declaring void and then returning a value, or forgetting to return on some path of a function with a return type.
- Using a reference where a return value would do.
Exercise
Write discountedPrice(float $price, int $percent = 0, ?float $maxDiscount = null): float. It applies the percentage, but never takes off more than $maxDiscount when that is set. Print the result for (2000), (2000, 10) and (2000, percent: 30, maxDiscount: 400).
Show one possible answer
<?php
function discountedPrice(float $price, int $percent = 0, ?float $maxDiscount = null): float
{
$discount = $price * $percent / 100;
if ($maxDiscount !== null) {
$discount = min($discount, $maxDiscount);
}
return $price - $discount;
}
echo discountedPrice(2000) . "\n";
echo discountedPrice(2000, 10) . "\n";
echo discountedPrice(2000, percent: 30, maxDiscount: 400) . "\n";2000
1800
1600Thirty percent of 2000 is 600, but the cap limits the discount to 400.
Key points
- A default value makes a parameter optional: function tax(float $price, float $rate = 0.18).
- Named arguments, tax(price: 100, rate: 0.05), let you skip optional parameters and make calls self-explanatory.
- Type declarations (int, float, string, bool, array, ?string, int|float, mixed) and return types catch wrong values early.
- ...$values collects any number of arguments into an array; ...$array spreads an array into arguments.