Что делает declare(strict_types=1) на самом деле
Директива declare(strict_types=1) должна стоять первой исполняемой строкой файла, сразу после открывающего тега <?php. Она не включает типизацию как таковую — типы параметров и возврата можно указывать и без неё. Она меняет только одно: как PHP ведёт себя, когда переданное значение не совпадает по типу с объявленным.
Без strict_types PHP пытается привести значение автоматически: строку "42" превратит в int 42, число 1 — в bool true. Со strict_types=1 любое несовпадение типа, кроме безопасного расширения int в float, завершается исключением TypeError сразу в момент вызова функции, а не где-то внутри неё после нескольких строк логики.
Слабая типизация: пример неявного приведения
Функция ниже объявляет параметр int, но без strict_types PHP примет и строку, и число с плавающей точкой, молча обрезав дробную часть.
function applyDiscount(int $percent): float {
return 100 - $percent;
}
echo applyDiscount("15abc"); // сработает как applyDiscount(15), без ошибки
Строка "15abc" не число, но PHP в нестрогом режиме отрежет часть, которая похожа на число, и выполнит функцию. Ошибка проявится не здесь, а там, где результат окажется неожиданно неверным — искать её придётся уже по логам, как описано в статье о разборе логов PHP.
Со strict_types=1: та же ошибка ловится сразу
Тот же код с добавленной директивой в начале файла ведёт себя иначе — вместо тихого приведения происходит явный отказ.
declare(strict_types=1);
function applyDiscount(int $percent): float {
return 100 - $percent;
}
echo applyDiscount("15abc");
// TypeError: applyDiscount(): Argument #1 ($percent) must be of type int, string given
Ошибка возникает на месте вызова, с точным номером строки, а не после нескольких шагов бизнес-логики. Это особенно важно для кода, написанного под PHP 8.3 и 8.4 — новые возможности этих версий разобраны в статье о PHP 8.3 и 8.4 и рассчитаны на строгую типизацию по умолчанию.
Типы свойств, параметров и возврата: примеры
| Конструкция | Пример | Что проверяет strict_types |
|---|---|---|
| Тип параметра | function f(int $x) | тип аргумента при вызове |
| Тип возврата | function f(): string | тип значения после return |
| Тип свойства класса | private float $price | тип при присваивании полю |
| Nullable-тип | function f(?int $x) | допускает int или null, не строку |
Nullable-типы и union-типы вида int|string работают со strict_types так же строго: любое значение, не входящее в объявленный список, вызывает TypeError.
Где строгая типизация ломает существующий код
- Данные из $_GET и $_POST всегда приходят строками — их нужно явно приводить через (int) перед передачей в типизированную функцию.
- Значения из PDO по умолчанию тоже строки, даже для числовых колонок — без явного приведения strict_types выбросит TypeError.
- Старые библиотеки без объявленных типов параметров продолжают работать как раньше, strict_types их не затрагивает.
- JSON после json_decode отдаёт числа как int или float в зависимости от содержимого — проверяйте тип перед передачей дальше.
- Тесты, написанные с расчётом на автоматическое приведение типов, начинают падать и требуют явных приведений в моках.
Итог: чек-лист внедрения strict_types
- declare(strict_types=1) стоит первой строкой после <?php в каждом файле проекта.
- Все входящие данные из $_GET, $_POST и PDO приводятся к нужному типу явно, а не полагаются на автоприведение.
- Параметры и возвращаемые значения публичных методов имеют объявленные типы, включая nullable и union.
- Новый TypeError отслеживается через error_log, а не через display_errors на проде.
- Изменение внедрено постепенно, по одному модулю, с прогоном тестов после каждого файла.