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

Вы подключили статический анализатор к проекту на 200 тысяч строк PHP, запустили на максимальном уровне и получили 4000 ошибок. Половина — реальные баги (обращение к свойству null, неверное число аргументов), половина — шум от кода, который анализатор не понимает. Дальше вопрос не в том, «нужен ли анализ», а в том, какой инструмент даст меньше ложных срабатываний на вашем коде и сколько времени уйдёт на приведение базы к чистому состоянию. PHPStan и Psalm решают одну задачу, но выбирают разные компромиссы.
Что вообще делает статический анализатор
Он читает код, не запуская его, и строит модель типов: что возвращает функция, чем может быть переменная в каждой точке, где вызов заведомо приведёт к ошибке. PHP — язык с динамической типизацией и историей из PHP 5, поэтому анализатору приходится опираться на PHPDoc-аннотации там, где нативных типов нет.
Оба инструмента — PHPStan (актуальна ветка 1.x, готовится 2.x) и Psalm (актуальна ветка 5.x) — работают через уровни строгости. Чем выше уровень, тем больше проверок включено и тем больше ошибок вы увидите. У PHPStan уровни от 0 до 9 (плюс отдельный флаг для строгих правил), у Psalm — от 8 (самый мягкий) до 1 (самый строгий), то есть шкала идёт в обратную сторону.
Ключевое различие в философии
Psalm изначально агрессивнее в проверке типов: у него богаче система дженериков и литеральных типов, он умеет отслеживать taint-анализ1 — путь недоверенных данных от источника (например, $_GET) до опасной операции (SQL-запрос). PHPStan исторически был мягче на старте и делал ставку на расширяемость через плагины для конкретных фреймворков.
Сегодня разрыв сократился: PHPStan научился дженерикам, Psalm обзавёлся большой экосистемой. Но разница в дефолтном поведении и в скорости осталась.
Четыре точки, где они расходятся
- Скорость. PHPStan обычно быстрее на первом проходе и агрессивнее кеширует результат в
tmpDir. Psalm многопоточный из коробки (флаг--threads), что помогает на больших базах и многоядерных машинах. - Ложные срабатывания. На «грязном» легаси-коде без аннотаций Psalm на строгих уровнях выдаёт заметно больше замечаний. PHPStan на том же уровне часто спокойнее — это плюс для постепенного внедрения и минус, если вам нужна максимальная строгость.
- Интеграция с фреймворками. Для Laravel есть
larastan(PHPStan), для Symfony — официальные расширения обоих. Магические методы Eloquent и контейнер Symfony без плагина анализатор понимать не будет и завалит вас ошибками об «неизвестных методах». - Подавление ошибок. Psalm поддерживает docblock-аннотации
@psalm-suppressпрямо над строкой. PHPStan использует inline-комментарии@phpstan-ignoreи файл-базлайн. У обоих есть baseline — снимок текущих ошибок, чтобы включить анализ на старом проекте и падать только на новых.
Baseline — это не «исправили», а «договорились пока не трогать». Он фиксирует технический долг в числе, а не убирает его. Опасность в том, что baseline тихо растёт годами.
Сравнение по параметрам
| Параметр | PHPStan 1.x | Psalm 5.x |
|---|---|---|
| Шкала уровней | 0–9, выше = строже | 8–1, ниже = строже |
| Многопоточность | нет (один процесс) | есть, --threads |
| Дженерики в PHPDoc | да, @template | да, @template |
| Taint-анализ (поиск инъекций) | в расширении | встроен, --taint-analysis |
| Baseline | neon-файл | xml-файл |
| Плагин для Laravel | larastan | psalm-plugin-laravel |
| Строгость на легаси по умолчанию | мягче | жёстче |
Как выглядит конфиг и запуск
Оба ставятся через Composer как dev-зависимость. PHPStan:
composer require --dev phpstan/phpstan
# минимальный phpstan.neon в корне проекта
parameters:
level: 6
paths:
- src
- tests
# запуск
vendor/bin/phpstan analyse
Psalm инициализирует конфиг командой, которая сама подбирает стартовый уровень по коду:
composer require --dev vimeo/psalm
# создаёт psalm.xml и предлагает уровень
vendor/bin/psalm --init
# запуск
vendor/bin/psalm
Файл psalm.xml задаёт уровень атрибутом errorLevel в корневом элементе <psalm> и список каталогов в блоке <projectFiles>. Точный формат смотрите в документации Psalm — он менялся между мажорными версиями, и копировать чужой конфиг вслепую не стоит.
Пример, который один найдёт, а другой пропустит
Возьмём код, где тип уточняется через дженерик-аннотацию:
<?php
/**
* @template T
* @param array<int, T> $items
* @return T|null
*/
function firstOrNull(array $items)
{
return $items[0] ?? null;
}
$users = firstOrNull([new User(), new User()]);
// анализатор знает: $users имеет тип User|null
$users->getName(); // ошибка: возможен вызов метода на null
Оба инструмента на достаточно строгом уровне поймают вызов метода на потенциальном null. Но именно точность вывода типа T через дженерик — та область, где Psalm традиционно был впереди, а PHPStan догнал в поздних релизах ветки 1.x. Если ваш код плотно использует коллекции с дженериками, проверьте оба на реальном модуле, а не на игрушечном примере.
Когда что выбирать
- Новый проект, хотите максимум строгости с первого дня — Psalm на низком уровне ошибок. Вы платите большим числом замечаний, но получаете дисциплину типов сразу.
- Большой легаси без аннотаций, нужно внедрить безболезненно — PHPStan, старт с уровня 0–1, baseline на текущие ошибки, постепенный подъём уровня. PHPStan мягче на старте и его проще продать команде.
- Laravel-проект — larastan (PHPStan) сегодня зрелее и популярнее в этой экосистеме.
- Нужен поиск потенциальных инъекций — Psalm со встроенным taint-анализом даёт это без плагина.
- Приоритет — скорость на большой многоядерной CI-машине — Psalm с
--threadsраспараллелит анализ.
Ставить оба сразу не надо: они будут дублировать проверки и удваивать время CI. Выберите один, доведите проект до чистого прохода, потом решайте, нужен ли второй ради специфичной проверки.
Что ломается при внедрении
- Магические методы.
__call,__get, ActiveRecord-модели, фасады Laravel — без плагина анализатор считает их несуществующими и заваливает ошибками. Ставьте расширение для фреймворка до первого запуска. - Baseline растёт. Разработчик добавляет код, видит ошибку, добавляет строку в baseline вместо исправления. Через полгода baseline — свалка. Лечится ревью-правилом: рост baseline требует явного согласования.
- Разные результаты на CI и локально. Причина почти всегда в версии PHP или в кеше. Фиксируйте версию анализатора в
composer.lockи чистите кеш в CI. - Переусложнённые аннотации. Погоня за высшим уровнем приводит к PHPDoc длиннее самой функции. Если аннотация сложнее кода, который она описывает, уровень выбран слишком высокий для этого места.
Что делать дальше
Возьмите один модуль проекта и прогоните оба анализатора на среднем уровне строгости — PHPStan level 5–6 и Psalm на уровне ошибок 3–4. Сравните не общее число замечаний, а долю ложных срабатываний: инструмент, который меньше врёт на вашем коде, и стоит внедрять. Дальше добавьте выбранный анализатор в CI как обязательную проверку с baseline, зафиксируйте текущий уровень и планово поднимайте его — по одному шагу за спринт, а не всё сразу. Следующий показатель, за которым стоит смотреть, — размер baseline: он должен уменьшаться, а не расти.
1 Taint-анализ — отслеживание «заражённых» (недоверенных) данных от точки входа до потенциально опасной операции, чтобы найти места возможных SQL-инъекций и XSS без запуска кода.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: PHPStan — официальная документация, Psalm — официальная документация
Частые вопросы
А если у меня старый PHP, скажем 7.4 — эти анализаторы запустятся?
Что делать, если анализатор выдаёт тысячи ошибок и это невозможно исправить?
Можно ли использовать PHPStan и Psalm одновременно?
Анализатор ругается на методы Laravel-модели, которых он не видит. Как быть?
Заменяет ли статический анализ тесты?
Результаты на CI и на моей машине отличаются — почему?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.