Type declarations
In the last lesson the function coffee took anything. A machine does not work that way: the coin slot is cut for a coin of one shape, and a shirt button will not fit through it.
A function without types has no such slot. A function triple($n) with the body return $n * 3; will take the string 'five' too, and the error happens later, inside, at the multiplication: Unsupported operand types: string * int. Such a message does not show who passed the string or where it came from.
That is why parameters get a type, and PHP checks the argument at the entrance, on every call.
Parameter type and result type
A type declaration is a type's name before a parameter or after the parameter list. Here is triple with types:
<?php
function triple(int $n): int
{
return $n * 3;
}
echo triple(4), "\n"; // 12
echo triple('5'), "\n"; // 15
echo triple(2.0), "\n"; // 6Let's take the declaration apart:
int $n: the parameter$ntakes a whole number;: intafter the parentheses: the function promises to return a whole number;- if a function with
: intreturns the string'abc', the call ends with aTypeError.
Look at the second and third calls: the string '5' and the fraction 2.0 got through too. With no special settings, PHP works in coercive mode: if an argument can be turned into the needed type, PHP turns it.
What converts and what does not
Here is how coercive mode treats arguments for an int parameter:
'5'becomes5: the string holds a number;2.0becomes2: there is no fractional part;2.5becomes2, but PHP printsDeprecated: Implicit conversion from float 2.5 to int loses precision;'five'becomes nothing: the string holds no number, and the call throws an error.
echo triple('five'), "\n"; // ✗Fatal error: Uncaught TypeError: triple(): Argument #1 ($n) must be of type int, string givenThe message names the function, the argument's number and the parameter's name, and at the end what came instead of int. This is a fatal error: the program stops at this line, and the error shows up where the wrong argument was passed.
Coercive mode is like an attendant at the machine: he changes a note with the digit '5' into a 5 coin and hands back a note with the word 'five'. But an attendant can make a mistake, while PHP's rules are fixed: a numeric string always passes, a non-numeric one never does.
Other types
You can declare any type you already know, and a few new ones:
string,float,bool,array: a string, a fraction, a boolean, an array;mixed: any value; it says outright that anything will do;int|string: a union type, which accepts a value of any of the listed types;void, for the result only: the function returns nothing and, for example, only prints.
There is also never: it marks a function that never hands control back, for example one that always ends with an error.
A type with null: ?int
A question mark before a type allows null as well. The parameter ?string $name takes a string or null:
function greet(?string $name): string
{
return 'Hi, ' . ($name ?? 'guest');
}
echo greet('Anna'), "\n"; // Hi, Anna
echo greet(null), "\n"; // Hi, guestWhat to know about this notation:
?intis the same asint|null: one and the same type and one and the same check;?goes before a single type only, so a union of two types withnullis written out in full:int|string|null;- writing
?int|stringis a parse error, and the file will not run.
Interviewers like to ask how ?int differs from int|null. The answer: only in how it is written, since the ? shorthand works for one type only.
Common mistake
People think the question mark makes a parameter optional and call the function without an argument:
echo greet(), "\n"; // ✗Fatal error: Uncaught ArgumentCountError: Too few arguments to function greet(), 0 passed and exactly 1 expected?string only says which values may be passed, not whether you may pass nothing. A default value makes a parameter optional: ?string $name = null. With it, greet() prints Hi, guest.
Key points
int $nand: intdeclare the types of a parameter and a result, and PHP checks them on every call.- In coercive mode,
'5'and2.0become5and2, while'five'gives aTypeError. ?intis the same asint|null, and a union of several types withnullis written asint|string|null.- The
?sign does not make a parameter optional: that takes a default value.