Строгая типизация в PHP 8: понятное руководство с примерами и ошибками

Строгая типизация в PHP 8: понятное руководство с примерами и ошибками

Строгая типизация в PHP 8: понятное руководство с примерами и ошибками

Зачем нужна строгая типизация в PHP 8

Ключевая проблема «слабого» PHP — неочевидные преобразования типов. PHP 8 привнёс зрелую типизацию: строгий режим, расширенные типы и типизированные свойства. Это повышает надёжность, делает код самодокументируемым и облегчает рефакторинг.

declare(strict_types=1): как включить строгий режим

Строгая типизация включается на уровне файла и должна стоять первой строчкой. В строгом режиме PHP не будет автоматически приводить типы аргументов функций и методов — вместо этого бросит TypeError.

<?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 в strict-режиме, в слабом — вернуло бы 5

Важно: строгий режим действует только в текущем файле. Если вы подключаете файлы без declare(strict_types=1), их поведение останется «слабым».

Типы аргументов и возвращаемых значений

Базовые скалярные типы: int, float, string, bool. Также доступны array, object, callable, iterable, self, parent и static (для методов класса). Для возврата значения указывайте тип после двоеточия.

<?php
declare(strict_types=1);

function sanitizeName(?string $name): string {
    // ?string означает: string|null
    $name = $name ?? '';
    return trim($name);
}

function average(array $numbers): float {
    if ($numbers === []) {
        return 0.0;
    }
    return array_sum($numbers) / count($numbers);
}

Советы:

  • Старайтесь типизировать всё: параметры, возвращаемые значения и свойства.
  • Используйте ?T только там, где реально допускаете null.
  • Уточняйте возвращаемые типы — это облегчает тестирование и рефакторинг.
  • Union types (объединения) и nullable

    Union types позволяют принять несколько вариантов типов. Для «может быть null» используйте короткую форму ?T, которая равна T|null.

    <?php
    declare(strict_types=1);
    
    function findUser(int|string $id): array {
        // Можно передать 42 или '42-uuid'
        // ... запрос к БД или репозиторию
        return ['id' => (string)$id, 'name' => 'Alice'];
    }
    
    function decode(?string $json): array {
        if ($json === null || $json === '') {
            return [];
        }
        $data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
        return is_array($data) ? $data : [];
    }
    

    Когда полезно union types:

  • API, где идентификатор бывает числовым или строковым (UUID).
  • Миграции кода: постепенно сужайте типы, сохраняя обратную совместимость.
  • Типизированные свойства и readonly

    Начиная с PHP 7.4 можно типизировать свойства, а с 8.1 — помечать их readonly (устанавливаются один раз). Это защита от случайной записи и отличный способ документировать модель данных.

    <?php
    declare(strict_types=1);
    
    class User {
        public int $id;
        public readonly string $email;
    
        public function __construct(int $id, string $email) {
            $this->id = $id;
            $this->email = strtolower($email);
        }
    }
    
    $u = new User(1, 'TEST@MAIL.COM');
    $u->id = 2;          // ОК
    $u->email = 'x@x';   // Error: cannot modify readonly property
    

    Плюсы: меньше «магии», больше инвариантов и ранних ошибок на этапе разработки.

    Специальные типы: mixed, never, static

  • mixed — «любой тип». Используйте, если вход может быть чем угодно (например, внешний ввод). Постарайтесь как можно раньше сузить mixed до конкретного типа.
  • never — функция не возвращается (например, делает redirect и завершает выполнение).
  • static — для методов, возвращающих экземпляр текущего класса (флюентный интерфейс, наследование).
  • <?php
    declare(strict_types=1);
    
    function redirect(string $url): never {
        header('Location: ' . $url);
        exit; // гарантирует never
    }
    
    class Builder {
        public function withId(int $id): static {
            // ...
            return $this; // static полезен при наследовании Builder
        }
    }
    

    Частые ошибки и как их исправить

  • Забыли declare(strict_types=1). Итог — скрытая коэрция. Решение: включайте строгий режим в каждом новом файле, добавьте проверку линтером/CI.
  • Избыточный null. Не размножайте ?T без необходимости. Если параметр обязателен — делайте обязательным и валидируйте раньше.
  • Слишком общий mixed. Вводите DTO/Value Object с типами вместо «массива всего».
  • Неверная сигнатура интерфейсов. При переопределении методов наследники обязаны сохранять совместимую сигнатуру типов.
  • Ловите TypeError там, где это нужно. В контроллерах или точке входа можно преобразовать исключение в понятный ответ API.
  • Практический чеклист по типизации

  • В каждом файле: declare(strict_types=1).
  • Типизируйте параметры и возвращаемые значения во всех публичных методах.
  • Используйте union types там, где их ждут потребители API.
  • Свойства — типизированные; для констант и неизменных полей — readonly.
  • Выносите «сырой» ввод в слой валидации, внутрь домена передавайте уже строгие типы.
  • Подключите статический анализ (PHPStan/Psalm) и повысите уровень строгости постепенно.
  • SEO-итог: типизация в PHP 8 на реальных примерах

    Строгая типизация в PHP 8 — это не «мода», а способ писать надёжный и предсказуемый код. Начните с declare(strict_types=1), задайте типы в публичном API, примените union types и readonly-свойства — и количество багов из‑за «магического» приведения типов резко снизится.

    Хотите быстро закрепить практику и закрыть пробелы? Попробуйте практический курс с заданиями и проектом: Прокачать PHP 8 и MySQL на реальных проектах (курс «с Нуля до Гуру 3.0»).

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий