Расскажите про типизацию в PHP
Что проверяют
Понимаете ли вы разницу между динамической типизацией и приведением типов, и что на самом деле делает declare(strict_types=1). Вопрос базовый, но по ответу хорошо видно, следит ли кандидат за современным PHP или застрял в эпохе, когда типы в сигнатурах были экзотикой.
Как отвечать
PHP типизирован динамически: тип принадлежит значению, а не переменной, и одна переменная спокойно меняет тип по ходу выполнения. При этом объявлять типы можно давно и почти везде: параметры и возвращаемые значения появились в седьмой версии, типизированные свойства в 7.4, дальше добавились объединения типов, never, пересечения и типизированные константы классов.
Ключевая часть ответа это два режима проверки:
- по умолчанию действует режим приведения: если значение можно привести к объявленному типу без потери данных, PHP это сделает молча;
- строгий режим включается директивой
declare(strict_types=1);первой инструкцией файла и приведение отключает, за одним исключением:intв параметр типаfloatпройдёт.
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
add("2", 3); // TypeError, в режиме приведения вернулось бы 5Важная деталь: директива действует на вызовы, сделанные из этого файла, а не на объявления функций в нём. Дженериков в языке нет, поэтому типы элементов коллекций описывают в аннотациях и проверяют статическим анализатором, в рантайме их не проверяет никто.
Где спотыкаются
Говорят, что строгий режим делает PHP строго типизированным языком, и думают, что он включается на весь проект одной настройкой. Директива файловая, и в проекте спокойно соседствуют файлы с ней и без неё.
Путают проверку типов с проверкой на этапе компиляции. Несоответствие всплывает в момент вызова и приводит к TypeError, поэтому типы в сигнатурах не отменяют ни тестов, ни статического анализа.
Полагаются на нестрогое сравнение. Оператор == приводит операнды, правила сравнения строки с числом в восьмой версии изменились, и код, который годами «работал», начинает вести себя иначе. По умолчанию стоит писать ===. Сюда же неявный null: параметр с дефолтом null надо объявлять явно как ?int.