Пых
8.26K subscribers
263 photos
16 videos
6 files
579 links
Блог Валентина Удальцова о разработке на PHP.

Хобот @phpyhobot
https://youtube.com/@phpyh
https://vkvideo.ru/@phpyh
https://t.iss.one/isPHPdying

Статистика: https://t.iss.one/INOTAROBOT?start=st1219340804

Для связи используйте личные сообщения канала.
Download Telegram
🎤 Сегодня я выступаю в Нижнем на митапе про организацию кода!

В 11:10 Денис Юрьев (Skysmart от Skyeng) расскажет, как переделать Symfony-проект в целое направление.

В 12:05 я расскажу, как структурировать код, чтобы не получить «большой ком грязи». Обсудим разные подходы к структурированию, принципы проектирования пакетов и инструменты для контроля.

Если вы в Нижнем, то приходите в Лекторий Правда, если нет — подключайтесь к трансляции на канале PHP Point!

https://youtu.be/SycSx0Qp3eg?t=3876
Continuous Integration

CI — must have для проекта любого размера. CI повышает качество кодовой базы, дисциплинирует команду и сокращает количество и продолжительность ревью.

Идеи проверок на базе нашего пайплайна в Happy Inc.:
• кодстайл (PHP CS Fixer, PHP_CodeSniffer),
• статический анализ (Psalm, PHPStan, PHPMD),
• валидность composer.json/lock (composer validate),
• наличие лишних пакетов (composer-unused),
• отсутствие пакетов в списке явных зависимостей (ComposerRequireChecker),
• уязвимости в пакетах (Roave Security Advisories, Local PHP Security Checker),
• синтаксис Yaml-файлов (Symfony Yaml),
• синтаксис Twig-шаблонов в проектах на Symfony (bin/console lint:twig),
• соответствие типов инъекций контейнера Symfony (bin/console lint:container),
• депрекации сервисов и конфигов Symfony (bin/console debug:container --deprecations),
• маппинг Doctrine и соответствие ему схемы БД (bin/console doctrine:schema:validate),
• конфигурация Doctrine для production (bin/console doctrine:ensure-production-settings --env=prod),
• связность/зацепление и направление зависимостей (Deptrac, dePHPend),
• ну и конечно же, тесты!

Также обратите внимание на репозиторий Static analysis tools for PHP и доклад 25+ инструментов для аудита кода.
👍3
API Payload Naming Convention

Всё очень просто: чем меньше трансформаций, тем лучше.

Например, вы проектируете API для сервиса на PHP. Ваши потенциальные клиенты — фронт на TypeScript, приложение для Android на Kotlin и приложение для iPhone на Swift. Беглым гуглением выясняем, что все четверо для свойств объектов используют camelCase. Стоит ли выбирать snake_case? Нет.

Только в случае легаси API имеет смысл дальше строгать эндпойнты в том же стиле, чтобы избежать винегрета.
Какой стиль именования используете для API на текущем проекте?
Anonymous Poll
62%
camelCase
24%
snake_case
2%
PascalCase
13%
kebab-case
Продвинутая интерполяция

Иногда в длинную строку нужно подставить константы / статические свойства или результаты функций / статических методов.

Первой на ум приходит интерполяция, но в PHP она невозможна без переменной, даже при использовании фигурных скобок. То есть вот так нельзя: echo "Число Эйлера: {M_E}". Можно для всех выражений объявить переменные и уже их интерполировать, но получится слишком громоздко. Можно оформить через sprintf, но в многочисленных безымянных %s и %20$d будет легко запутаться.

Костыль Выход есть! Присвоим переменной имя функции strval. Мы получили "интерполятор" callable(mixed): string, при помощи которого в литерал можно подставить любое приводимое к строке выражение:

$intrp = 'strval';
echo "Число Эйлера: {$intrp(M_E)}";

Если пойти чуть дальше и обернуть sprintf в класс с __invoke, зафиксировав первый аргумент (partial application!), получится мощный универсальный интерполятор.

https://3v4l.org/DogHB
❤️ PHP Russia 2021

В понедельник прошла конференция PHP Russia. В том, что она состоялась сейчас, на гребне второй волны, огромная заслуга организаторов. Подход был настолько серьёзным, насколько это можно себе представить.

Я не мог приехать к началу и остаться на афтерпати, но даже несколько часов на площадке были кромешным кайфом. Как спикер я оценил техническую подготовку — всё было быстро, уверенно и качественно. Как участник — организацию, атмосферу и программу.

Огромное спасибо @samdark, @Halfnomad и @pronskiy за глубокое понимание и поддержку! С таким программным комитетом можно успешно выступить прямо в аду.

А ещё я был рад видеть вас, подписчиков, в зале! Легко и приятно на сцене, когда с половиной аудитории ты давно в диалоге.

Я счастлив как слон, до встречи на следующей конференции! 🤗
#[Route(name: self::class)]

Лайфхак для симфонистов. Если вы размещаете каждый экшн в отдельном invokable классе, то его имя можно использовать в качестве имени роута.

При таком подходе не нужно соблюдать никакие конвенции именования и хардкодить строки.

Работает и с атрибутами, и с аннотациями, однако в аннотациях нельзя использовать self, только само имя класса. На скрине оба примера.
LSP для конструктора

__construct — особенный метод класса в PHP. В частности, он не подчиняется LSP, то есть в подклассе сигнатура конструктора может быть изменена как угодно. Это удобно в большинстве случаев, но не всегда.

Иногда в базовом классе мы хотим предложить статическую фабрику. Чтобы она работала корректно, конструкторы подклассов должны соблюдать LSP. Есть два варианта, как это гарантировать.

1️⃣ Зафиналить конструктор в базовом классе. Минус в том, что мы не сможем инициализировать состояние в подклассе. Можно, конечно, добавить в базовый класс костыль в виде метода initialize, но есть решение поэлегантнее.

2️⃣ Попросить Psalm проверять LSP для __construct при помощи @psalm-consistent-constructor. Если конструктор подкласса не будет соблюдать сигнатуру, Psalm выбросит ConstructorSignatureMismatch.

https://psalm.dev/docs/running_psalm/issues/UnsafeInstantiation/
Кодишь 2.0

Мама, я в телевизоре!

Большое спасибо организаторам конференции Кодишь 2.0 за приглашение и отличные выходные в Брянске!

https://youtu.be/tBgpNqkoN_k

P.S. Thesis ну прям очень скоро будет, в поезде многое доделал.
Тесты должны быть независимыми

Юнит-тесты должны быть независимыми по определению. Всё необходимое инициализируется в блоке Arrange и утилизируется из памяти после Assert. Использовать в юнитах setUp / tearDown я не рекомендую.

Тесты, работающие с общим состоянием, должны после выполнения возвращать его в исходную точку. Для этого можно использовать in-memory хранилища или откатывать транзакции.

Чтобы защитить себя от неявных зависимостей между тестами, следует запускать их в случайном порядке. Для этого прописываем <phpunit executionOrder="random"> в phpunit.xml или выполняем phpunit с флагом --order-by=random.
?-> вместо NullObject

Иногда вместо NullObject удобно использовать null-safe оператор ?->.
Пых
Photo
?-> вместо NullObject: по следам дискуссии

Предыдущий пост бурно и полезно обсудили, расписываю основные мысли.

"За" и "против" использования ?-> для опциональных зависимостей:
самый лаконичный способ до выхода PHP 8.1 (там можно будет использовать new для значений параметров по умолчанию);
обходится дешевле в рантайме, так как в null-сценарии выполняется меньше кода (сразу оговорюсь, что это вообще сомнительный аргумент, в частности, трудно себе представить приложение без логгирования в продакшне);
скрытый и "ненужный" if;
NullObject — более корректное и универсальное с точки зрения ООП решение, null-safe подход может быть уместен только в нескольких случаях;
на проекте без статического анализатора вероятность ошибки выше.

Также в Пыхтелке затронули очень важную тему: зависимости редко бывают опциональными. Действительно, nullable зависимости часто сигнализируют о нарушении SRP. Необязательные поведения лучше оформлять в виде декораторов или middleware — их легко включать и выключать, не изменяя код (см. OCP).

Что касается логгирования, его не всегда можно представить в виде декоратора. Зачастую полезная для логов информация инкапсулирована в реализации и не протекает через контракт. Скорее всего, в этом случае тоже где-то нарушается SRP, но добавление абстракции исключительно ради логгирования может быть неоправданным усложнением.

И последний нюанс: зачем вообще делать логгер опциональной зависимостью, если он всегда есть в контейнере? Мой ответ простой: лень. В юнит-тестах, не тестирующих само логгирование, не хочется каждый раз писать new Service(/** required dependencies, */ new NullLogger()) ☺️.

Итак, теперь мы знаем, что можно вот так нестандартно использовать ?->, но что это не всегда хорошая идея. Всем спасибо за комментарии!
Ищу к себе в команду middle/senior разработчика!

В Happy Inc мы исследуем вовлечённость и лояльность персонала в крупных компаниях: проводим анонимные опросы, строим сложные отчёты, доставляем их клиентам в различных форматах.

У нас модульный монолит без легаси. PHP 8.1 / Symfony 6.0 / PostgreSQL 14 / OpenAPI / CQRS / Event Sourcing / Psalm / Thesis и всё, о чём я тут рассказываю. 😜

Full-time, офис и удалёнка, ЗП по результатам собеса.

Резюме мне в ЛС @vudaltsov.
👍9🔥1
Ночное образовательное шоу IT Nights 3.0

Приглашаю сегодня всех в 19:00 на IT Nights. Будут спикеры из Facebook, Яндекса, JetBrains, VK, X5 Group.

Меня заинтриговал доклад Изменения — единственная константа в IT и рыцарский турнир, в котором примут участия ребята с Podlodka, Егор Толстой и Стас Цыганов.

По промокоду phpyh скидка 20%.