In this article, we will discuss function arguments, default values, type unions, argument unpacking, pass by value or reference, and finally, introduced in PHP 8, named arguments.
To define function parameters
We can define function parameters within the parentheses of the function definition.
function foo($x, $y)
{
return $x * $y;
}
When we call the foo()
function, we need to pass two arguments.
echo foo(5, 10); // 50
Parameters are the variables listed in the function definition, while arguments are the actual values we pass to the function when we invoke it; in the example above, $x
and $y
are the function's parameters, and 5
and 10
are the passed arguments, i.e., the values assigned to the parameters of the foo()
function.
Type Hinting
Type Hinting is a feature introduced by PHP starting from version 5, which allows specifying the type of object/data passed as an argument to a function or method.
Starting from PHP version 7, in addition to the weak mode, there is also the strict mode. By enabling this mode, we force PHP to interpret variable types, and if the type is not satisfied, we will receive a fatal error.
So, if in the example above, we want to be more restrictive and accept only the int
type and no others, we should declare it at the top of the code using the declare
statement followed by strict_types=1
.
declare(strict_types=1);
function foo(int $x, int $y)
{
return $x * $y;
}
Now, if we try to pass an argument of type float
or string
, we will receive an error.
echo foo(5.0, '10');
// Fatal error: Uncaught TypeError: foo(): Argument #1 must be of type int, float given, called in ...
Union of types
Starting from PHP version 8, it is possible to accept multiple data types. We can specify type unions using the |
character.
declare(strict_types=1);
function foo(int|float $x, int|float $y)
{
return $x * $y;
}
echo foo(5.0, 10); // 50
In this case, we can invoke the foo()
function by passing both integers and decimals as arguments.
Default values
For every parameter listed in the function definition, it is necessary to pass an argument; otherwise, we will receive a fatal error.
echo foo(5.0);
// Fatal error: Uncaught ArgumentCountError: Too few arguments to function foo(), 1 passed in ...
It is possible to set default values for parameters to assign a value in case it is not passed as an argument.
declare(strict_types=1);
function foo(int|float $x, int|float $y = 10)
{
return $x * $y;
}
echo foo(5.0); // 50
You can assign scalar arrays and null
values as default values, but not a function call or an object; it must be a constant expression.
Another important thing is that optional parameters must be defined after any required parameter.
Pass arguments by value vs by reference
As a default, arguments for required parameters are passed by value. You can specify to pass an argument by reference using the &
character in the parameter definition.
For example, let's define a foo()
function that takes 2 arguments and returns the multiplication of the arguments, dividing the first argument by 2 if it is an even number.
declare(strict_types=1);
function foo(int|float $x, int|float $y): int|float
{
if ($x % 2 === 0) {
$x /= 2;
}
return $x * $y;
}
$a = 6.0;
$b = 7;
echo foo($a, $b); // 21
var_dump($a, $b); // float(6) int(7)
We have mentioned that by default, arguments are passed by value. In fact, modifying the value of $x
($x /= 2;
), which is the first argument passed to the function, does not affect the original variable $a
, which still holds the value float(6)
.
If we change the definition of the first argument to pass the reference to the variable $a
, in this case, modifying the parameter $x
also affects the value of the original variable $a
.
declare(strict_types=1);
function foo(int|float &$x, int|float $y): int|float
{
if ($x % 2 === 2) {
$x /= 2;
}
return $x * $y;
}
$a = 6.0
$b = 7;
echo foo($a, $b); // 21
var_dump($a, $b); // float(3) int(7)
Decompression of topics
We want to define a function that returns the sum of 2 or more arguments. We can change the function definition as we add an argument, or we can use the spread operator that will capture any arguments passed to the function in an array.
declare(strict_types=1);
function sum(...$numbers): int|float
{
$sum = 0;
foreach ($numbers as $number) {
$sum += $number;
}
return $sum;
//return array_sum($numbers);
}
$a = 6.0
$b = 7;
echo sum($a, $b, 10, 25); // 48
You can use the spread operator even after a fixed number of parameters, in which case only the arguments passed after the fixed ones will be added to the array.
declare(strict_types=1);
function sum(int|float &$x, int|float $y, ...$numbers): int|float
{
return $x + $y + array_sum($numbers);
}
$a = 6.0
$b = 7;
echo sum($a, $b, 10, 25); // 48
In the example above, $x
and $y
are equal to 6.0
and 7
, respectively, and all the remaining arguments passed to the function are captured within the $numbers
array.
We can specify the type of the extra arguments passed to the function.
declare(strict_types=1);
function sum(int|float &$x, int|float $y, int|float...$numbers): int|float
{
return $x + $y + array_sum($numbers);
}
$a = 6.0
$b = 7;
echo sum($a, $b, 10, 25, '8');
// Fatal error: Uncaught TypeError: sum(): Argument #5 must be of type int|float, string given, called in ...
In this case, we receive an error because the last argument passed to the function is a string.
The spread operator can also be used to unpack an array into the list of arguments to be passed to a function.
declare(strict_types=1);
function sum(int|float &$x, int|float $y, int|float...$numbers): int|float
{
return $x + $y + array_sum($numbers);
}
$a = 6.0
$b = 7;
$numbers = [10, 25, 8];
echo sum($a, $b, ...$numbers); // 56
named arguments
In PHP 8.0, we have the ability to specify the name of the argument to be passed to the function, regardless of the order listed in the function definition.
Let's define a function where, if the first parameter $x
is divisible by the second parameter $y
, it returns their division; otherwise, it returns the value of $x
.
declare(strict_types=1);
function foo(int &$x, int $y): int
{
if ($x % $y === 0) {
return $x / $y;
}
return $x;
}
$a = 6
$b = 3;
echo foo($a, $b); // 2
What happens if we change the order of the arguments passed to the function?
echo foo($b, $a); // 3
We get the value of $b
because it's no longer divisible. This new feature in PHP 8 effectively allows us to name the argument to indicate the value of the respective parameter declared in the function, regardless of the order.
echo foo(y: $b, x: $a); // 2
This feature proves to be very useful in various use cases. For example, if we want to change the order of parameter definitions, we would have to search throughout the code where the function is used and update the order of how we pass arguments. With named arguments, we no longer need to worry about updating existing functions because the order is not important. However, if we change the name of a parameter, we still need to update the name in every part of the code where it is used.
Another very valid use case is when you have a series of parameters with default values. For example, PHP has a function called setcookie()
that has many parameters with default values. If we wanted to pass only the name
and the last argument, we would still have to pass all the others defined before the last one.
/*
method signature
setcookie (
string $name,
string $value = "",
int $expires = 0,
string $path = "",
string $domain = "",
bool $secure = false,
bool $httponly = false,
) : bool
*/
setcookie('test', '', 0, '', '', false, true);
// named arguments
setcookie(name: 'test', httponly: true);
Passing the same argument multiple times results in an error because it overwrites the previous argument.
echo foo(x: $b, x: $a);
// Fatal error: Uncaught Error: Named parameter $x overwrites previous argoument in ...
You can combine named arguments with positional arguments as long as the named arguments come after the positional ones.
echo foo($a, y: $b); // 2
Passing x
again conflicts because we already have an argument for the $x
parameter.
echo foo($a, x: $b);
// Fatal error: Uncaught Error: Named parameter $x overwrites previous argoument in ...
Another thing to note is that if we use argument unpacking and the array contains keys, those keys will be treated as argument names.
$arr = ['y': 3, 'x' => 6];
echo foo(...$arr); // 2
Good work 👨💻
Top comments (2)
A couple of tiny copy-paste issues with your demo code:
if ($x % 2 === 2)
will never be true, since$x % 2
can only be an int in the range [0, 1].You're missing a semicolon after
$a = 6.0
@moopet thank you for the report; I have made the necessary changes to the code