Що насправді робить 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 на проді.
- Зміну впроваджено поступово, по одному модулю, із прогоном тестів після кожного файлу.