PHP 7.4: Строгая типизация против Слабой

Источник: «Strict typing vs. Weak typing in PHP»
В PHP 5 ввели объявление типов (также известное как подсказки типов), которые позволяют функциям требовать, чтобы параметры имели определённый тип во время вызова. Если заданное значение имеет неправильный тип, то генерируется ошибка: в PHP 5 — исправимая фатальная ошибка, а в PHP 7 — исключение TypeError.

Статья написана в 2020 году, когда PHP 7.4 ещё широко использовался. На днях завершилась поддержка PHP 7.4 и все должны были перейти на PHP 8.0, но в силу тех или иных причин многие продолжают использовать PHP 7.4, а иногда и более ранние версии. В связи с этим статья остаётся актуальной и сейчас.

TL;DR Используйте строгий режим

Слабая типизация PHP

Объявление типов можно указать, добавив тип перед параметрами функции, например так:

function sum(int $a, int $b)
{
return $a + $b;
}

Итак, теперь всякий раз, когда вызывается метод sum PHP будет ожидать получение параметров соответствующих указанным типам. Например:

var_dump(sum(3, 7)) // 10

Это нормально, и теперь вы ожидаете, что следующее вызовет какую-то ошибку, когда мы используем подсказку типов…

var_dump(sum(3.5, 7.5))

Но вот загвоздка! PHP не выдаст при этом никакой ошибки, потому что по умолчанию PHP будет преобразовывать значения неправильного типа в ожидаемый скаляр типа, если это возможно. Например, функции в качестве параметра передано float, которая ожидает integer, получит переменную типа integer. Другими словами, PHP будет пытаться откатиться к целевому типу всякий раз, когда это возможно. Это называется Слабая типизация.

Таким образом предыдущий пример выведет результат без каких-либо сбоев.

var_dump(sum(3.5, 7.5)) // 10
^ ^
3 7

Даже строковое представление этих чисел будет работать нормально. Таким образом следующее тоже будет работать:

var_dump(sum("3.5", 8)) // 11

PHP попытается привести "3.5" к типу integer, что будет равно 3 и соответствующим образом обработает его.

Строгая типизация PHP

для строгой проверки типов, PHP позволяет включать строгий режим для каждого файла. В строгом режиме только переменная типа точно соответствующего объявленному будет принята, иначе будет выброшено исключение TypeError.

Чтобы включить строгий режим, используется оператор declare с объявлением strict_types, следующим образом.

declare(strict_types=1);

function sum(int $a, int $b)
{
return $a + $b;
}

try {
var_dump(sum(1, 2));
var_dump(sum("1.5", 3));
} catch (TypeError $e) {
echo 'Error: '.$e->getMessage();
}

// int(3)
// Error: Argument 1 passed to sum() must be of the type int, string given, called in [...][...] on line 11

Как видите, при задании строкового значения там, где функция ожидает целочисленное значение, PHP теперь выдаст фатальную ошибку, которая была невозможно при слабой типизации.

Предостережения

Здесь следует отметить одну вещь: даже при строгой типизации функции может быть присвоено integer, когда она ожидает float. Это одно исключение из строгой типизации.

Кроме того, PHP не поддерживает псевдонимы для скалярных типов. Так в следующем примере…

declare(strict_types=1);

function foo(boolean $bar) {
return $bar;
}

try {
var_dump(foo(true));
} catch (TypeError $e) {
echo 'Error: '.$e->getMessage();
}

…выдаст следующую ошибку,

Error: Argument 1 passed to foo() must be an instance of boolean, bool given, called in [...][...] on line 10

Чтобы исправить это, нужно использовать тип bool вместо boolean при объявлении типа.

declare(strict_types=1);

function foo(bool $bar) {
return $bar;
}

try {
var_dump(foo(true));
} catch (TypeError $e) {
echo 'Error: '.$e->getMessage();
}

// bool(true)

И, наконец, включение строго режима повлияет не только на объявление типов функций, но и на объявление возвращаемых типов.

Как включить строгий режим глобально?

К сожалению, ни как. Вместо этого нужно добавить declare(strict_types=1); в начало каждого php-файла в котором вы хотите включить строгий режим.

Итак, если вы хотите включить строгий режим глобально во всём проекте, вы можете это сделать используя такие инструменты, как sed, awk или другой инструмент по вашему выбору, чтобы заменить все <?php на <?php declare(strict_types=1); в каталоге вашего проекта, как упоминал Никита Попов в этом комментарии.

Дополнительные материалы

Предыдущая Статья

Laravel: Все секреты миграции

Следующая Статья

Использование mkcert для включения https на локальных сайтах