Перейти к содержимому
Теория · Функции · урок 3 из 11

Объявления типов

В прошлом уроке функция coffee принимала что угодно. Автомат так не работает: прорезь для монет сделана под монету определённой формы, и пуговицу в неё не просунуть.

У функции без типов такой прорези нет. Функция triple($n) с телом return $n * 3; примет и строку 'five', а ошибка случится уже внутри, на умножении: Unsupported operand types: string * int. По такому сообщению не видно, кто передал строку и откуда она взялась.

Поэтому параметрам указывают тип — и PHP проверяет аргумент на входе, при каждом вызове.

Тип параметра и тип результата

Объявление типа — это имя типа перед параметром или после списка параметров. Вот triple с типами:

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

Разберём объявление по частям:

  • int $n — параметр $n принимает целое число;
  • : int после скобок — функция обещает вернуть целое число;
  • если функция с : int вернёт строку 'abc', вызов закончится ошибкой TypeError.

Обратите внимание на второй и третий вызовы: строка '5' и дробное 2.0 тоже прошли. Без особых настроек PHP работает в режиме приведения: если аргумент можно превратить в нужный тип, PHP превращает его сам.

Что приводится, а что нет

Для параметра int режим приведения действует так:

  • '5' становится 5: в строке записано число;
  • 2.0 становится 2: дробной части нет;
  • 2.5 становится 2, но PHP выводит Deprecated: Implicit conversion from float 2.5 to int loses precision;
  • 'five' не становится ничем: числа в строке нет, и вызов бросает ошибку.
PHP
echo triple('five'), "\n"; // ✗
TEXT
Fatal error: Uncaught TypeError: triple(): Argument #1 ($n) must be of type int, string given

Сообщение называет функцию, номер аргумента и имя параметра, а в конце — что пришло вместо int. Это фатальная ошибка: программа останавливается на этой строке, и ошибка видна там, где передали неверный аргумент.

Режим приведения похож на служащего у автомата: бумажку с цифрой '5' он разменяет на монету 5, а бумажку со словом 'five' вернёт. Только служащий может ошибиться, а правила PHP постоянны: числовая строка проходит всегда, нечисловая — никогда.

Другие типы

Объявить можно любой тип, который вы уже знаете, и несколько новых:

  • string, float, bool, array — строка, дробное число, логическое значение, массив;
  • mixed — любое значение: так прямо пишут, что подойдёт всё;
  • int|string — объединение типов: подходит значение любого из перечисленных типов;
  • void — только для результата: функция ничего не возвращает и лишь выполняет действие, например печатает.

Есть и never: так помечают функцию, которая никогда не возвращает управление — например, всегда завершается ошибкой.

Тип с null: ?int

Знак вопроса перед типом разрешает ещё и null. Параметр ?string $name принимает строку или null:

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

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

У записи ? три правила:

  • ?int — то же самое, что int|null: один и тот же тип и одна и та же проверка;
  • ? ставится только перед одним типом, поэтому объединение двух типов с null пишут полностью: int|string|null;
  • запись ?int|string — ошибка разбора, и файл не запустится.

На собеседовании любят спрашивать, чем ?int отличается от int|null. Ответ: ничем, кроме записи — сокращение ? годится только для одного типа.

Частая ошибка

Считают, что знак вопроса делает параметр необязательным, и вызывают функцию без аргумента:

PHP
echo greet(), "\n"; // ✗
TEXT
Fatal error: Uncaught ArgumentCountError: Too few arguments to function greet(), 0 passed and exactly 1 expected

?string говорит только о том, какие значения можно передать, а не о том, можно ли не передавать ничего. Необязательным параметр делает значение по умолчанию: ?string $name = null. С ним greet() напечатает Hi, guest.

Главное

  • int $n и : int объявляют типы параметра и результата, и PHP проверяет их при каждом вызове.
  • В режиме приведения '5' и 2.0 становятся 5 и 2, а 'five' даёт TypeError.
  • ?int — то же, что int|null, а объединение нескольких типов с null пишут как int|string|null.
  • Знак ? не делает параметр необязательным: для этого нужно значение по умолчанию.