Skip to content
Theory · Functions · lesson 3 of 11

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
<?php

function triple(int $n): int
{
    return $n * 3;
}

echo triple(4), "\n"; // 12
echo triple('5'), "\n"; // 15
echo triple(2.0), "\n"; // 6

Let's take the declaration apart:

  • int $n: the parameter $n takes a whole number;
  • : int after the parentheses: the function promises to return a whole number;
  • if a function with : int returns the string 'abc', the call ends with a TypeError.

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' becomes 5: the string holds a number;
  • 2.0 becomes 2: there is no fractional part;
  • 2.5 becomes 2, but PHP prints Deprecated: Implicit conversion from float 2.5 to int loses precision;
  • 'five' becomes nothing: the string holds no number, and the call throws an error.
PHP
echo triple('five'), "\n"; // ✗
TEXT
Fatal error: Uncaught TypeError: triple(): Argument #1 ($n) must be of type int, string given

The 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:

PHP
function greet(?string $name): string
{
    return 'Hi, ' . ($name ?? 'guest');
}

echo greet('Anna'), "\n"; // Hi, Anna
echo greet(null), "\n"; // Hi, guest

What to know about this notation:

  • ?int is the same as int|null: one and the same type and one and the same check;
  • ? goes before a single type only, so a union of two types with null is written out in full: int|string|null;
  • writing ?int|string is 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:

PHP
echo greet(), "\n"; // ✗
TEXT
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 $n and : int declare the types of a parameter and a result, and PHP checks them on every call.
  • In coercive mode, '5' and 2.0 become 5 and 2, while 'five' gives a TypeError.
  • ?int is the same as int|null, and a union of several types with null is written as int|string|null.
  • The ? sign does not make a parameter optional: that takes a default value.