PHP 7.4: Строгая типизация против Слабой
Статья написана в 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);
в каталоге вашего проекта, как упоминал Никита Попов в этом комментарии.