Применение строгой проверки типов в PHP (strict_types)
Строгая типизация в PHP
В PHP по умолчанию действует нестрогая (coercive) типизация: при передаче аргументов в функции или выполнении операций PHP пытается автоматически преобразовать значение к ожидаемому типу. Например, число 5 может быть воспринято как строка '5'. Это удобно, но может привести к скрытым ошибкам. Строгая типизация (declare(strict_types=1)) отключает неявные преобразования типов, заставляя разработчика явно указывать соответствие. Это повышает надёжность кода, особенно в больших проектах.
Как включить строгую проверку типов на уровне файла?
Основной способ - добавить в начало файла (до любого другого кода, кроме открывающего тега <?) директиву declare(strict_types=1);. После этого все вызовы функций и методов в этом файле будут проверять соответствие типов строго.
<?php
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
echo add(2, 3); // 5
echo add('2', 3); // TypeError: Argument 1 must be of type int, string givenPhp mime type (mime-типы в php)
Второй вызов вызовет ошибку TypeError, так как строка '2' не соответствует типу int. Чтобы избежать ошибки, необходимо явно преобразовать значение: add((int)'2', 3).
Типичная проблема: забыли добавить директиву declare в начало файла - строгая типизация не работает. Решение: всегда ставить declare(strict_types=1) первым выражением после <?.
Ещё одна ошибка: попытка передать дробное число туда, где ожидается int. В строгом режиме это TypeError. Выход - явное преобразование: (int)3.14 или использование функций округления.
Почему PHP автоматически преобразует типы (coercive mode) и как это работает?
Без строгого режима PHP выполняет автоматические преобразования: строка '123' становится числом 123, логическое true - 1 и т.д. Это может быть удобно при быстрой разработке, но снижает предсказуемость.
<?php
function add(int $a, int $b): int {
return $a + $b;
}
echo add('10', '20'); // 30Of type string is deprecated php (предупреждение об устаревании типа string в php)
Здесь строки '10' и '20' автоматически преобразованы в целые числа. Результат - 30. В нестрогом режиме такая ситуация не вызовет ошибку, но потенциально может скрыть логическую ошибку, если ожидалась конкатенация.
Проблема: неожиданное поведение при передаче нечисловых строк, например 'abc'. В coercive mode она преобразуется в 0, что может привести к ошибкам вычислений. Решение: использовать строгую типизацию либо проверять входные данные.
Можно ли использовать type hints без строгого режима?
Да, можно - type hints работают в обоих режимах. Разница только в обработке несоответствия: в строгом - ошибка, в нестрогом - попытка преобразования. Например, если функция ожидает float, а передать int, в нестрогом режиме это пройдёт (int будет преобразован во float), а в строгом - нет.
<?php
function mul(float $x, float $y): float {
return $x * $y;
}
echo mul(2, 3); // 6 (int 2 и 3 преобразуются во float в нестрогом режиме)
// при declare(strict_types=1) этот код вызовет TypeErrorPhp check type (проверка типа переменной в php)
Рекомендуется всегда использовать строгий режим для большей определённости.
Как объединить файлы с разными режимами типизации?
Директива declare(strict_types=1) действует только на тот файл, в котором она написана. Если файл A включает файл B, то в файле B будет свой режим (если он не задан - по умолчанию нестрогий). Таким образом, можно комбинировать: в большинстве новых файлов ставить строгую типизацию, а legacy-файлы оставлять без неё.
<?php // file_a.php
declare(strict_types=1);
require 'file_b.php';
function test(int $x) { ... }
test('123'); // TypeError в file_a.php, но внутри файла file_b.php поведение зависит от его declareStrict type php (строгая типизация в php)
Проблема: функция, объявленная в файле со строгим режимом, может быть вызвана из файла без него, и тогда аргументы будут преобразованы до передачи в строгую функцию. Например, в file_b.php (coercive) вызывается test('123') из файла file_a.php (strict) - строка '123' будет передана без преобразования, и в file_a.php возникнет TypeError. Решение: обеспечивать единообразие режима в границах одного запроса или явно приводить типы на стороне вызывающего файла.
Как перехватить ошибку несоответствия типов?
TypeError - это исключение, которое можно отловить с помощью блока try/catch. Это позволяет graceful degrade или логирование.
<?php
declare(strict_types=1);
function div(int $a, int $b): int {
return intdiv($a, $b);
}
try {
echo div('10', 2);
} catch (TypeError $e) {
echo 'Ошибка: ' . $e->getMessage();
}Php type int (тип int в php)
Результат: Ошибка: Argument 1 passed to div() must be of the type int, string given.
Проблема: TypeError не всегда удобно обрабатывать в каждом файле. Можно использовать глобальный обработчик исключений или проверять типы вручную перед вызовом (is_int, is_float и т.д.).
Как работает strict_types в классах и методах?
Строгая типизация применима ко всем функциям и методам (включая конструкторы, статические методы), объявленным в файле с директивой. Типизированные свойства (PHP 7.4+) также подчиняются этому правилу.
<?php
declare(strict_types=1);
class Calculator {
protected int $precision = 2;
public function setPrecision(int $p): void {
$this->precision = $p;
}
}
$calc = new Calculator();
$calc->setPrecision('3'); // TypeError: Argument 1 must be of type int, string givenPhp return types (типы возвращаемых значений в php)
Свойство $precision объявлено как int, и при попытке присвоить строку в строгом режиме возникает ошибка, даже если присваивание происходит в том же файле.
Проблема: при наследовании или переопределении методов можно нарушить ковариантность/контравариантность типов. Строгая типизация не отменяет правила variant но увеличивает строгость.
Как разрешить несколько типов при строгой типизации?
PHP поддерживает union types (например, int|string) и mixed. Union types допускают значения любого из перечисленных типов. Это сочетание строгости и гибкости: параметр должен быть либо int, либо string, без автоматического преобразования между ними.
<?php
declare(strict_types=1);
function format(int|string $value): string {
return (string) $value;
}
echo format(42); // '42'
echo format('42'); // '42'
echo format(3.14); // TypeError: Argument 1 must be of type int|string, float givenPhp 7 types (типы данных в php 7)
Тип float не входит в union, поэтому будет ошибка. Это позволяет контролировать допустимые типы без потери строгости.
Проблема: слишком широкие union (int|string|float|bool) снижают строгость. Рекомендуется использовать только необходимые варианты. Для полной свободы можно применить тип mixed, но тогда теряется контроль.
Как обрабатывать возвращаемое значение void или never?
Тип void означает, что функция не возвращает значение. Тип never (PHP 8.1) - функция никогда не завершается нормально (выбрасывает исключение или завершает скрипт). Строгая типизация контролирует, чтобы ничего не возвращалось для void и чтобы завершение было нештатным для never.
<?php
declare(strict_types=1);
function logError(string $msg): void {
file_put_contents('log.txt', $msg);
}
// Попытка вернуть что-то из void вызовет ошибку
Расширенные примеры использования strict_types
1. Сравнение coercive и strict для разных типов
В coercive режиме PHP автоматически приводит строки к числам, а числа к строкам при необходимости. В strict режиме это запрещено.
<?php
// coervice.php (без declare)
function sum(int $a, int $b): int {
return $a + $b;
}
echo sum('5', '10'); // 15
?>
<?php
// strict.php
declare(strict_types=1);
function sumStrict(int $a, int $b): int {
return $a + $b;
}
echo sumStrict('5', '10'); // TypeError
?>
Результат для coervice.php: 15 Результат для strict.php: ошибка TypeError
2. Строгая типизация с nullable типами и значениями по умолчанию
Если параметр может быть null, его тип указывается с вопросительным знаком (начиная с PHP 7.1) или через union с null.
<?php
declare(strict_types=1);
function greet(?string $name): string {
return 'Hello, ' . ($name ?? 'guest');
}
echo greet('Alice'); // Hello, Alice
echo greet(null); // Hello, guest
echo greet(42); // TypeError: Argument 1 must be of type ?string, int given
?>
Hello, Alice Hello, guest TypeError
3. Union types с пересечением типов
Union позволяет передавать несколько разных типов. При этом автоматического приведения между ними не происходит - передаваемое значение должно точно соответствовать одному из типов union.
<?php
declare(strict_types=1);
function process(int|float $value): float {
return $value * 2;
}
echo process(5); // 10
echo process(2.5); // 5.0
echo process('5'); // TypeError: Argument 1 must be of type int|float, string given
?>
10 5.0 TypeError
4. Строгая типизация с void и never (PHP 8.1)
Тип never указывает, что функция никогда не возвращает управление (всегда вызывает exit/die или выбрасывает исключение).
<?php
declare(strict_types=1);
function terminate(string $msg): never {
echo $msg;
exit;
}
function safeDivision(int $a, int $b): int {
if ($b === 0) {
terminate('Division by zero');
}
return $a / $b;
}
echo safeDivision(10, 0); // Ничего не выведет, произойдёт exit
?>
Division by zero (скрипт завершится)
5. Взаимодействие строгих и нестрогих файлов при include
Файл с declare(strict_types=1) подключает файл без строгого режима. Важно понимать, что проверка типов происходит при определении функции (в её файле), а не при её вызове.
<?php // main_strict.php
declare(strict_types=1);
require 'helper_coervice.php';
function calculate(int $x): int { return $x * 2; }
echo calculate('5'); // TypeError в main_strict.php
echo increment('3'); // Функция increment из helper_coervice.php (без strict) получит строку '3', но в её файле нет strict, поэтому она преобразует строку в int
?>
<?php // helper_coervice.php
function increment(int $a): int {
return $a + 1;
}
?>
TypeError на строке echo calculate('5'); (строгий режим в main_strict.php не даёт преобразовать '5' в int).
Если вызвать increment('3'), то внутри helper_coervice.php (coervice) '3' будет преобразовано в 3, результат 4. Строгий режим main_strict.php на эту функцию не влияет.
6. Стрелочные функции (arrow functions) со строгими типами
Стрелочные функции также подчиняются строгой типизации, если файл объявлен как strict.
<?php
declare(strict_types=1);
$add = fn(int $a, int $b): int => $a + $b;
echo $add(4, 5); // 9
echo $add(4, '5'); // TypeError
?>
9 TypeError
7. Типизированные свойства класса (PHP 7.4+)
Свойства с указанием типа также проверяются в строгом режиме. Попытка присвоить значение несовместимого типа вызывает TypeError, даже если присваивание происходит внутри того же класса.
<?php
declare(strict_types=1);
class User {
public int $id;
public string $name;
}
$user = new User();
$user->id = 'abc'; // TypeError: Cannot assign string to property User::$id of type int
?>
TypeError
8. Использование strict_types в генераторах
Генераторы могут иметь типизированные параметры и возвращаемый тип (например, Generator). Тип возвращаемого значения генератора - это тип, который возвращает yield, но в строгом режиме типы элементов могут быть проверены.
<?php
declare(strict_types=1);
function rangeInt(int $start, int $end): Generator {
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}
foreach (rangeInt('1', 3) as $val) { // TypeError: Argument 1 must be of type int, string given
echo $val;
}
?>
TypeError, так как переданы строки '1' и '3'
9. Пользовательская обработка TypeError
Можно реализовать собственный класс, наследующий TypeError, для кастомизации сообщений или логики.
<?php
declare(strict_types=1);
class MyTypeError extends TypeError {}
function safeMultiply(int $a, int $b): int {
if (!is_int($a) || !is_int($b)) {
throw new MyTypeError('Only integers allowed');
}
return $a * $b;
}
try {
echo safeMultiply('x', 2);
} catch (MyTypeError $e) {
echo 'Поймана ошибка: ' . $e->getMessage();
}
?>
Поймана ошибка: Only integers allowed
10. Тип mixed и строгая типизация
mixed означает любой тип. В строгом режиме mixed принимает любое значение без неявных преобразований, но и без контроля.
<?php
declare(strict_types=1);
function debug(mixed $data): string {
return var_export($data, true);
}
echo debug('test'); // 'test'
echo debug(42); // 42
echo debug(null); // NULL
?>
'test' 42 NULL
Эти примеры демонстрируют, как строгая типизация в PHP помогает писать более предсказуемый код, требуя явного указания типов и предотвращая скрытые ошибки преобразования.