Pull to refresh

Comments 9

Абсолютно согласен с автором, по-поводу важности статического анализа. Для себя могу выделить две сложности:

  • Наличие false positives;

  • Огромное количество сообщений об ошибках;

  • Отсутствие состояние прогресса (не понятно когда завершится проверка)

В качестве решения для JavaScript использую:

  • ESLint настроенный исправлять, все что он находит;

  • 🐊Putout, который сообщает только о том, что может исправить сам;

Конечно, это не спасёт от ошибки вроде ParadoxicalCondition, описанной в статье, но с этим не плохо справляются тесты + coverage.

Конкретно с Psalm у нас довольно мало false-positives, а огромное количество ошибок прячется в baseline, который со временем постепенно худеет. Ведь при модификации кода практически невозможна ситуация, когда новое выражение точно попадёт в baseline. Это связано с форматом: Psalm фиксирует не только количество ошибок определённого типа в файле, но и конкретное выражение, которое их вызывает.

А вот с JS у нас пока ещё не всё хорошо, ESLint в принципе настроен, но не форсируется. Зато новый код всё больше пишется на Typescript, который сам очень помогает с контролем типов.

Очередной пруф, что лучше пыхи ничего и нет.

Сам подключал psalm на прошлом проекте. Было интересно попробовать. Но в итоге впечатление сложилось двоякое. С одной стороны круто иметь статическую типизацию на php. С другой сама экосистема к этому была не слишком приспособлена. Те же symfony / doctrine тогда не имели нужных аннотаций. Да и вообще куча кода была написана с ориентацией на динамическую природу php.


Я причем эмпирически выставил строгость проверки что-то около 3. И в основном приходилось бороться с "вопросиками". Т.е. избавляться от nullable. И я до сих пор не до конца понимаю, как это нужно правильно делать, например, для сущностей доктрины. Т.е. все не nullable свойства с точки зрения типов нужно инициализировать в конструкторе. Но это сильно выбивается из общей практики. То же самое с автоинкрементным id. При создании новой сущности он должен быть null. Но потом этот вопросик сильно мешается в коде. Приходится добавлять во все сущности getInitializedId() :int с ассертом внутри. И так много в чем. Когда с точки зрения контекста использования ты знаешь лучше анализатора, что за реальный тип у этого значения.

Я на одном небольшом проекте для себя успешно подружил Psalm с сущностями доктрины. Исходил я примерно из таких принципов:

Если какое-то поле у сущности не может быть null, то оно должно быть проинициализировано в конструкторе. То есть логически, если у нас User не может быть без почты и пароля, тогда конструктор будет их принимать.

Я подумал, что для $id использовал @psalm-suppress PropertyNotSetInConstructor, но заглянул в код и, оказывается, нет. $id честно прописан ?int, и даже это где-то используется для того, чтобы отличать уже записанные в БД сущности от только что сгенерированных. А там, где я точно уверен, что он не null, писал (int)$id.

Вероятно, это можно всё как-то сделать на магии шаблонов, чтобы $em->find() возвращал специально шаблонизированную Entity, у которой $id не может быть null, но быстро набросать у меня не получилось.

Ну и в любом случае, это немного костыльно решается плагином.

Может и можно через дженерики возвращать сущность из entityManager с не nullable id. Но при инжекте в контроллер с помощью ArgumentResolver уже придется прописывать дженерик вручную. Да и потом везде его по типам пропихивать. Так что трейт с getInitializedId с ассертом внутри кажется меньшей проблемой.


А ведь кроме id у сущности может быть куча автозаполняемых полей. CreatedAt updatedAt, createdBy… Часто они в doctrine event subscriber автоматом заполняются. Но тогда их тоже нужно делать nullable.


Я на одном небольшом проекте для себя успешно подружил Psalm с сущностями доктрины.

На моем текущем проекте у сущности может быть несколько десятков полей. Ну, пусть даже 10 из них не nullable. Как-то сомнительно их все через конструктор инициализировать. Да и формы с таким по дефолту не очень дружат. Не знаю насчет апи платформы и прочих библиотек..

На моем текущем проекте у сущности может быть несколько десятков полей. Ну, пусть даже 10 из них не nullable. Как-то сомнительно их все через конструктор инициализировать.

Вот тут как та ситуация, когда код, который никак не хочет укладыватся в правила статического анализа, имеет некоторые архитектурные проблемы. С точки зрения идеальной архитектуры объект с не-nullable свойствами не может существовать, если у них нет значений. Тут просится что-то промежуточное типа builder'а с валидатором внутри. Но это в идеальном мире, конечно... У нас, кстати, часть таких проблем с пользовательским вводом решена с помощью spatie/data-transfer-object, но это довольно специфичное решение.

Но с точки зрения реального, а не идеального кода всё совсем не так :))) Но осознавать неидеальность, конечно, тоже небесползено.

Мы на своём проекте тоже внедрили psalm около года назад. Как боролись с большим количеством ошибок - заигнорили в конфиге псалма все файлы проекта и по одному снимали из игнора, правили, вынимали из игнора следующий файл и т.д. Так постепенно вычистили все замечания псалма. На сегодня псалм выполняется на travis CI, автоматически проверяя запушеный код. Сейчас выставлен level 4, нам хватает. Какой уровень выставлен у вас?

Между прочим, весьма успешная альтернативная стратегия!

У нас level 1, писать новый "чистый" код, в общем-то, все довольно быстро привыкли.

Sign up to leave a comment.