Как использовать 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