Как использовать PHPStan для обеспечения качества и чистоты кода на PHP

Узнайте, как использовать PHPStan для выявления ошибок типов еще до запуска кода. Статья охватывает основы настройки уровней строгости и продвинутые техники работы с Generics и Stub-файлами.

Введение

В современной веб-разработке динамическая типизация PHP долгое время считалась преимуществом, обеспечивающим гибкость и скорость разработки. Однако по мере роста сложности архитектуры проектов эта особенность начинает нести в себе скрытые риски: ошибки типов могут проявляться только во время выполнения (runtime), что затрудняет отладку и снижает общую надежность системы. Статический анализ становится необходимым инструментом для предотвращения подобных проблем, позволяя выявлять потенциальные баги еще до запуска кода.

PHPStan утвердился как стандарт индустрии для обеспечения чистоты и предсказуемости PHP-кода. Он позволяет разработчикам автоматизировать проверку типов, находить логические несоответствия и следовать лучшим практикам программирования без необходимости написания бесконечных юнит-тестов на базовые проверки. Использование подобных инструментов не только повышает качество продукта, но и способствует формированию более строгой инженерной культуры внутри команды.

В данной статье мы подробно разберем возможности PHPStan: от основ настройки уровней строгости до продвинутых техник работы с Generics, Type Aliases и Stub-файлами. Кроме того, мы рассмотрим практические аспекты интеграции статического анализа в CI/CD пайплайны, чтобы превратить проверку качества кода в неотъемлемую часть процесса разработки.

Основы работы с PHPStan: уровни строгости и конфигурация

PHPStan предоставляет гибкую систему настройки для балансировки между скоростью разработки и качеством кода. Ключевым инструментом управления этим процессом является система уровней строгости, которые варьируются от 0 до 9.

Система уровней анализа

Каждый уровень добавляет новые правила проверки:

  • Уровень 0–2: Базовая проверка. Анализирует существование классов, методов и свойств, а также базовую совместимость типов аргументов.
  • Уровни 3–5: Средняя строгость. Проверяет возвращаемые типы функций, типизацию параметров в методах и корректность вызовов внутри циклов.
  • Уровни 6–9: Максимальная строгоness. Требует полной типизации всех свойств и методов (включая массивы), а также строгой обработки типа mixed на уровне 9.

Для существующих проектов рекомендуется использовать стратегию постепенного перехода: начните с уровня 2 или 3, чтобы выявить критические ошибки, и постепенно повышайте уровень по мере проведения рефакторинга.

Конфигурация через phpstan.neon

Все настройки инструмента описываются в файле конфигурации (обычно phpstan.neon). Здесь можно указать пути к анализируемым директориям, исключить сторонние библиотеки или настроить специфические параметры:

parameters:
    level: 5
    paths:
        - src
        - tests
    excludePaths:
        - src/Migrations/*
        - vendor
    checkMissingIterableValueType: true
    treatPhpDocTypesAsCertain: false

Использование файла конфигурации позволяет стандартизировать процесс анализа для всей команды разработки и легко интегрировать его в CI.

Типичные ошибки на ранних этапах

Даже на низких уровнях строгости PHPStan эффективно выявляет распространенные баги, которые могут привести к Fatal Error в продакшене:

  • Обращение к методам или свойствам классов, которых не существует.
  • Передача аргументов несовместимых типов (например, передача объекта вместо строки).
  • Вызов методов у переменных, которые могут принимать значение null (проверка на "nullable" типы).

Продвинутые возможности: Generics, Type Aliases и Stub-файлы

Для достижения максимального уровня покрытия статического анализа в крупных проектах на PHP недостаточно просто указывать типы аргументов. Необходимо использовать инструменты, которые позволяют описывать сложные структуры данных и взаимодействовать с внешним кодом без потери типобезопасности.

Использование дженериков (Generics)

Дженерики позволяют параметризовать типы в классах и методах. В контексте PHPStan это критически важно для работы с коллекциями, репозиториями и фабриками. Вместо того чтобы помечать массив как array<mixed> или использовать неточный object[], дженерики позволяют анализатору «пробрасывать» конкретный тип через цепочку вызовов.

/**
 * @template T of object
 */
class Collection {
    /** @var array<T> */
    private array $items = [];

    /** @param T $item */
    public function add(object $item): void {
        $this->items[] = $item;
    }

    /** @return T|null */
    public function first(): ?object {
        return $this->items[0] ?? null;
    }
}

// Теперь PHPStan знает, что в коллекции находятся только объекты User
/** @var Collection<User> $userCollection */
$userCollection = new Collection();
$userCollection->add(new User());
$user = $userCollection->first(); // Анализатор понимает, что $user — это экземпляр User