Инструменты проверки качества PHP-кода: что реально встроить в CI
PHPStan, Psalm, PHP-CS-Fixer, PHP_CodeSniffer, Rector и PHPMD решают разные задачи и редко нужны все сразу. Разбираем, какой набор и уровень строгости имеет смысл в CI-пайплайне.

Инструменты проверки качества PHP-кода делятся на несколько несвязанных категорий: статический анализ типов, форматирование, обнаружение запахов кода и автоматический рефакторинг. Попытка включить все инструменты сразу на максимальной строгости обычно заканчивается сотнями ошибок в первый день и заброшенным пайплайном через месяц. Разберём, что каждый инструмент решает и в каком порядке их стоит внедрять.
Прежде чем выбирать конкретные инструменты, полезно отделить вопрос «что проверять» от вопроса «когда это должно блокировать сборку». Форматирование и явные ошибки типов имеет смысл делать обязательными с первого дня, а более субъективные метрики вроде сложности метода или числа параметров лучше оставлять предупреждением, пока команда не выработает общее мнение о допустимых порогах для своего конкретного проекта и его особенностей.
Ещё один частый источник провала внедрения: запуск всех инструментов локально у каждого разработчика без единой версии конфигурации в репозитории. Если у одного в команде PHPStan уровня 5, а у другого уровня 8 на локальной машине, результаты ревью начинают расходиться, и доверие к инструменту падает быстрее, чем успевает принести пользу. Конфигурация анализатора должна лежать в репозитории и быть одинаковой для всех, включая CI.
Статический анализ: PHPStan и Psalm
PHPStan и Psalm ищут противоречия типов до запуска кода: вызов метода на возможном null, передачу аргумента неподходящего типа, обращение к несуществующему свойству. Оба инструмента поддерживают уровни строгости, и именно выбор стартового уровня определяет, станет ли проверка частью рабочего процесса или будет отключена при первом неудобстве.
# Низкий уровень для старого проекта, чтобы начать без шока
vendor/bin/phpstan analyse src --level=2
# Для нового кода разумно сразу выше
vendor/bin/phpstan analyse src --level=8| Инструмент | Основная задача | Когда предпочесть |
|---|---|---|
| PHPStan | Статический анализ типов, расширяемые правила | Большинство проектов, особенно с фреймворками через готовые расширения |
| Psalm | Статический анализ типов, таинты для безопасности | Проекты, где важна проверка путей данных на уязвимости |
| Rector | Автоматический рефакторинг и миграция синтаксиса | Массовое обновление кода под новую версию PHP |
Форматирование: PHP-CS-Fixer и PHP_CodeSniffer
Споры о расстановке скобок и отступов стоят команде рабочих часов, если их не автоматизировать. PHP-CS-Fixer приводит код к выбранному набору правил и умеет применять правки автоматически, а PHP_CodeSniffer исторически силён в отчётах о нарушениях стандарта и в написании собственных правил проверки. Для большинства проектов достаточно одного инструмента форматирования, запускать оба параллельно избыточно.
# Применить форматирование по правилам PSR-12
vendor/bin/php-cs-fixer fix src --rules=@PSR12
# Проверить без применения правок (для CI)
vendor/bin/php-cs-fixer fix src --dry-run --diffПоиск запахов кода: PHPMD и PHPCPD
В отличие от статического анализа типов, который почти всегда прав в своих выводах, инструменты поиска запахов кода дают рекомендации, а не окончательные истины. Длинный метод с высокой цикломатической сложностью не обязательно плох, если он описывает действительно сложный, но линейный бизнес-процесс без ветвлений состояния. Команде стоит воспринимать такие отчёты как повод присмотреться, а не как обязательную задачу для немедленного рефакторинга.
PHPMD (PHP Mess Detector) находит типичные признаки плохого дизайна: длинные методы, слишком много параметров, булевы флаги-аргументы. PHPCPD (PHP Copy Paste Detector) ищет буквально продублированные участки кода. Важно помнить, что найденное дублирование не автоматически означает нарушение DRY: иногда это совпадение синтаксиса, а не повторение знания, и решение нужно принимать по контексту, а не вслепую сливать совпавшие блоки.
- PHPMD хорошо встраивается как предупреждение, не блокирующее сборку, на старте внедрения
- PHPCPD полезен при работе с легаси для поиска кандидатов на рефакторинг
- Оба инструмента дают сигнал для ревью, а не окончательный вердикт
Один нюанс часто упускают при обсуждении PHPMD и PHPCPD вместе: найденное дублирование кода не равно нарушению принципа DRY в его строгом смысле. Иногда два похожих блока описывают совершенно разные бизнес-правила, которые просто выглядят одинаково на уровне синтаксиса, и слепое слияние таких блоков по рекомендации инструмента способно создать более хрупкую конструкцию, чем исходное дублирование.
Автоматический рефакторинг: Rector
Rector способен не только находить устаревшие конструкции, но и переписывать их автоматически по заданным правилам: заменять each() на foreach, добавлять типы параметров по выведенным сигнатурам, переводить код на синтаксис конструктора с промоутированными свойствами. При миграции на новую версию PHP, например с 8.1 на 8.3, Rector закрывает значительную часть механической работы, которую иначе пришлось бы делать руками.
# Применить набор правил миграции на PHP 8.3
vendor/bin/rector process src --set=php83
# Сначала посмотреть diff без применения
vendor/bin/rector process src --set=php83 --dry-runОтдельный вопрос, который команды часто упускают: что делать с существующими нарушениями на старте внедрения нового инструмента. Если требовать немедленного исправления всех найденных проблем, разработчики либо застрянут на недели, либо начнут массово подавлять предупреждения через игнор-комментарии, что хуже, чем отсутствие проверки вовсе, потому что создаёт иллюзию контроля без реального покрытия.
Как собрать это в CI без лишнего шума
Включение всех инструментов на полной строгости в первый день гарантированно похоронит инициативу под сотнями предупреждений, которые никто не будет читать. Рабочая стратегия: зафиксировать текущий уровень нарушений как базовую линию, запретить её рост, и постепенно повышать строгость только для новых и изменённых файлов.
- Подключить PHP-CS-Fixer в режиме dry-run как обязательную проверку, автоматизировав форматирование локально через git-хук
- Запустить PHPStan или Psalm на низком уровне, зафиксировать baseline существующих ошибок
- Добавить PHPMD как предупреждение, не блокирующее сборку, для постепенного знакомства команды с отчётами
- Поднимать уровень строгости PHPStan раз в несколько недель, а не разово
- Запускать Rector вручную перед плановыми миграциями версии PHP, а не как часть обычного CI
Эти инструменты дополняют друг друга, а не заменяют тесты и ревью кода: статический анализ находит противоречия типов, которые тесты обычно даже не пытаются покрыть, но ничего не скажет о том, правильно ли выбрана бизнес-логика. Хорошо настроенный набор из трёх-четырёх инструментов, внедряемых постепенно, приносит больше пользы, чем полный набор, включённый сразу и быстро заброшенный командой.
Стоит периодически пересматривать состав набора: инструмент, который год назад ловил реальные ошибки, со временем может начать генерировать в основном шум, если команда уже устранила типичные для проекта проблемы. Поддержка качества кода работает лучше как постоянный процесс с регулярной настройкой порогов, а не как разовое внедрение набора плагинов в CI.
Имеет смысл назначить в команде человека, который раз в квартал пересматривает пороги и baseline-файлы этих инструментов, а не оставлять их настройками, однажды выставленными и забытыми навсегда. Пятнадцать минут такого ревью на старте следующего спринта обычно экономят часы споров о том, почему инструмент внезапно начал ругаться на код, который раньше проходил проверку без единого замечания.
В конечном счёте ценность этих инструментов измеряется не числом подключённых плагинов, а тем, сколько реальных проблем они помогли поймать до код-ревью и до продакшена. Команда, которая внедряет их постепенно и с пониманием, получает в CI не формальность для галочки, а действительно полезный второй взгляд на каждое изменение, который со временем экономит куда больше часов, чем было потрачено на первоначальную настройку и последующую донастройку правил под особенности конкретного проекта.
Частые вопросы
Нужно ли использовать PHPStan и Psalm одновременно?
Обычно нет. Оба решают похожую задачу статического анализа типов, и выбор одного из них снижает дублирование усилий. Исключение, если команде отдельно важны таинты безопасности Psalm.
С какого уровня строгости PHPStan стоит начинать на старом проекте?
С низкого, например 2 или 3, зафиксировав текущие ошибки как baseline. Резкий переход на максимальный уровень на легаси обычно приводит к сотням нерелевантных предупреждений и отказу от инструмента.
Заменяет ли статический анализ модульные тесты?
Нет. Статический анализ находит несоответствия типов до запуска кода, а тесты проверяют корректность поведения во время выполнения. Это дополняющие друг друга проверки, а не альтернативы.



