Какой стиль оформления значений в исключениях лучше читается?
Anonymous Poll
27%
Cannot tag `MyService` (`src/config.php:23`) with `MyTag` during tag resolution
21%
Cannot tag "MyService" ("src/config.php:23") with "MyTag" during tag resolution
11%
Cannot tag 'MyService' ('src/config.php:23') with 'MyTag' during tag resolution
14%
Cannot tag MyService (src/config.php:23) with MyTag during tag resolution
0%
Напишу свой вариант в комментариях
27%
Смотреть результаты
Please open Telegram to view this post
VIEW IN TELEGRAM
😁8👍3
Forwarded from Пыхник’26 — PHP на природе
This media is not supported in your browser
VIEW IN TELEGRAM
👍6💩5🔥4❤2
Пыхник’26 — PHP на природе
Video message
Скидывайте идеи футболок под этим сообщением.👇
Please open Telegram to view this post
VIEW IN TELEGRAM
🖕8👍2
Прошло полтора года с тех пор, как я всерьёз начал пилить DI контейнер для Thesis. Набор требований у меня был большой:
Модульность
Всё есть модуль, включая само приложение.
PHP DSL
Выразительный и лаконичный, не на массивах.
Типобезопасность
Не строковые идентификаторы, а объекты с дженериком. Легко написать плагин статанализатора для тайпчека аргументов.
Импорт/экспорт
У модуля прозрачный типизированный контракт на вход (конфигурация) и на выход (экспорт сервисов).
Scoped сервисы
В асинхронном приложении можно писать код как в умирающем: инжектить репозиторий по интерфейсу в конструктор обработчика и не замечать, что у него под капотом транзакция, открытая специально для текущего сообщения.
Disposer-коллбэки
К сервису, не меняя его код, можно привязать функцию, которая будет вызвана по завершении скоупа. Удобно для закрытия ресурсов и остановки фоновых корутин.
Предсказуемый автовайринг
Модуль из vendor не может внезапно затереть сервис в модуле проекта. Все биндинги локализованы, нет глобального пространства имён.
Автоконфигурация
Возможность порефлексировать сервисы перед сборкой и по подтипу или атрибуту накинуть теги.
Теги
Имплементируют
Tag<T> и содержат типизированные данные. Через хук можно собрать протегированные сервисы, преобразовать их и заинжектить куда угодно.First-class callable сервисы
myFunc(...) и $service->method(...) легко объявить сервисами, частично применить аргументы, прочитать атрибуты и подключить как обработчики сообщений, не написав CompilerPass на 1000 строк.И вот мы здесь. 30 июня, я только что поставил тег 0.5.0 с осознанием, что до 1.0 рукой подать и я готов с вами поделиться!
В README есть всё необходимое для первого знакоства, в
docs/ поподробнее про часть аспектов. Буду рад скоро постримить и показать больше!https://github.com/thesis-php/dic
https://github.com/thesis-php/symfony-console-module
/**
* @implements Module<Ref<Application>>
*/
final readonly class App implements Module
{
public function configure(Dic $dic): mixed
{
$dic->apply(new AutoconfigureCommands());
$dic->function(self::greet(...));
return $dic->import(new SymfonyConsoleModule(name: 'Пых'));
}
#[AsCommand('greet', aliases: ['g'])]
private static function greet(#[Argument] string $name, SymfonyStyle $io): int
{
$io->title("Hello, {$name}!");
return Command::SUCCESS;
}
}
exit(Dic::run(
module: new App(),
main: static fn(Application $app) => $app->run(),
));
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥37❤7👍7🤔3😱1💩1😭1
Forwarded from Пыхник’26 — PHP на природе
Please open Telegram to view this post
VIEW IN TELEGRAM
💩27🔥9❤2👍1
Forwarded from Пыхник’26 — PHP на природе
Please open Telegram to view this post
VIEW IN TELEGRAM
💩28🔥9
Forwarded from Пыхник’26 — PHP на природе
Media is too big
VIEW IN TELEGRAM
Запустили краудфандинг!!!
11 сентября в Art Village под Москвой собираем небольшую PHP-конференцию на 120 участников: один поток, 8 докладов и много живого общения.
Средства на мероприятие собираем через краудфандинг. Можно взять офлайн-билет, доступ к записям, футболку «Вы вымрете, а я останусь» или просто поддержать проект любой суммой.
https://planeta.ru/campaigns/pyhnik26
11 сентября в Art Village под Москвой собираем небольшую PHP-конференцию на 120 участников: один поток, 8 докладов и много живого общения.
Средства на мероприятие собираем через краудфандинг. Можно взять офлайн-билет, доступ к записям, футболку «Вы вымрете, а я останусь» или просто поддержать проект любой суммой.
https://planeta.ru/campaigns/pyhnik26
👍23❤11🔥9💩9
Пых
Первый бенчмарк thesis/dic
Смоделировал следующий сценарий работы неумирающего приложения:
Сравнение, конечно, не совсем равнозначное, потому что только у Thesis есть этап диспоуза сервисов после скоупа и не во всех контейнерах есть полноценные scoped сервисы.
Но в целом результат ожидаемый. Скомпилированный контейнер Symfony почти эквалентен написанному руками коду (manual).
Thesis (current) быстрее Laravel и Yii3, потому что рефлексию и резолвинг графа зависимостей он делает на этапе сборки, а в рантайме вызывает полностью готовые фабрики.
Для бенчмарка попробовал🐘 Testo.
Код скину, когда доделаю ещё несколько важных сценариев. Накидывайте идей.
Смоделировал следующий сценарий работы неумирающего приложения:
Задепоили приложение в рантайме а-ля RoadRunner, собрали контейнер и готовы принимать запросы. Ни один сервис не инстанциирован, но контейнер максимально готов.
Как быстро испытуемые вернут инстанс контроллера с несколькими зависимостями?
Сравнение, конечно, не совсем равнозначное, потому что только у Thesis есть этап диспоуза сервисов после скоупа и не во всех контейнерах есть полноценные scoped сервисы.
Но в целом результат ожидаемый. Скомпилированный контейнер Symfony почти эквалентен написанному руками коду (manual).
Thesis (current) быстрее Laravel и Yii3, потому что рефлексию и резолвинг графа зависимостей он делает на этапе сборки, а в рантайме вызывает полностью готовые фабрики.
Для бенчмарка попробовал
#[Bench] в Код скину, когда доделаю ещё несколько важных сценариев. Накидывайте идей.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥23👍9👏4
This media is not supported in your browser
VIEW IN TELEGRAM
👍17🔥7💯5💩3❤2
Эквайринг Т-Банка, 2026 год
Повторное выставление счёта (ручка Init) бросает ошибку о том, что такой заказ уже есть, вместо того, чтобы идемпотентно вернуть ссылку на оплату (
Окей, допустим. План Б: оборачиваю
Но ни в одном из них нет PaymentURL!!!🤬
То есть у меня есть лишь один шанс записать
Ну как так-тонахуй ...
Решение, конечно, есть: при ретрае и ошибке о дубле создавать новый заказ с другим ID. Но это означает, что я уже не могу без таблички скоррелировать ID своего инвойса с ID, который использую для заказа на их стороне. А это дополнительное I/O на пустом месте, брошенные заказы в админке Т-Банка и усложнённое расследование инцидентов.
Если у кого-то есть друзья в Т-Банке, попросите исправить это недоразумение.
Повторное выставление счёта (ручка Init) бросает ошибку о том, что такой заказ уже есть, вместо того, чтобы идемпотентно вернуть ссылку на оплату (
PaymentURL).Окей, допустим. План Б: оборачиваю
Init в try/catch и при ошибке о дублирующемся заказе пробую GetState и CheckOrder, которые возвращают инфу о выставленном счёте.Но ни в одном из них нет PaymentURL!!!
То есть у меня есть лишь один шанс записать
PaymentURL после получения ответа от Т-Банка. А если ответ не дошёл по сети? А если мой сервер упал? А если БД нагружена? А если кролик отошёл погрызть морковку?Ну как так-то
Решение, конечно, есть: при ретрае и ошибке о дубле создавать новый заказ с другим ID. Но это означает, что я уже не могу без таблички скоррелировать ID своего инвойса с ID, который использую для заказа на их стороне. А это дополнительное I/O на пустом месте, брошенные заказы в админке Т-Банка и усложнённое расследование инцидентов.
Если у кого-то есть друзья в Т-Банке, попросите исправить это недоразумение.
Please open Telegram to view this post
VIEW IN TELEGRAM
1🤣56👍12🤡10❤5🔥4😡4🤔1🎉1🤮1💯1
or <=> if
Замешивая в🥟 Testo Алексея Гагарина тикет про исключения, обнаружил такой фрагмент:
Конформный пыхарь написал бы так:
Если что,
Почему
Конкретно в этом случае разницы нет. Но вообще оператор
Короче, как вам такой стиль кодирования?
Замешивая в
$classOrObject->getCode() === 0 or $result->withCode($classOrObject->getCode());
Конформный пыхарь написал бы так:
if ($classOrObject->getCode() !== 0) {
$result->withCode($classOrObject->getCode());
}
Если что,
withCode() мутирующий, $result = не пропущен.Почему
or, а не ||?Конкретно в этом случае разницы нет. Но вообще оператор
or на самом дне списка приоритетов, даже ниже, чем присваивание. Это позволяет писать так:
$x = foo() or bar(); // ($x = foo()) or bar();
$x = foo() || bar(); // $x = (foo() || bar());
Короче, как вам такой стиль кодирования?
Please open Telegram to view this post
VIEW IN TELEGRAM
php-testo.github.io
Testo — Modern PHP Testing Framework
PHP testing framework without TestCase inheritance. Clean OOP, middleware architecture, PSR-14 events, Assert/Expect facades.
👎78🥴27👍8🤯5🤮5🔥2❤1🙏1
Rector, это не то же самое!!!
Пошёл делать тикет и нашёл уже существующий с ровно такой же проблемой.
И знаете, что там ответил мейнтейнер?
Почему он неправ
Есть большая разница между состоянием, в котором объект оказывается при создании, и состоянием, в которое он может прийти по ходу выполнения логики. В моём случае
Иными словами, тип параметра описывает то, что допустимо на входе, а тип свойства — все состояния объекта за его жизнь. В первую очередь мы декларируем параметр и свойство по отдельности, и уже во вторую рассматриваем возможность склеить их в promoted, но только если типы эквивалентны.
Открыл новую issue с моими пояснениями, посмотрим, дойдёт или нет...
⸻
🌿 Приняли первый доклад на Пыхник’26!
Пошёл делать тикет и нашёл уже существующий с ровно такой же проблемой.
И знаете, что там ответил мейнтейнер?
That's expected, if the type not match, that means a design issue in the first place, if property is private and nullable, and filled via constructor, that means the constructor should be nullable param as well as it just assign -> fill, no nullable check.
Почему он неправ
Есть большая разница между состоянием, в котором объект оказывается при создании, и состоянием, в которое он может прийти по ходу выполнения логики. В моём случае
null означает «больше нет», а не «никогда не было».Иными словами, тип параметра описывает то, что допустимо на входе, а тип свойства — все состояния объекта за его жизнь. В первую очередь мы декларируем параметр и свойство по отдельности, и уже во вторую рассматриваем возможность склеить их в promoted, но только если типы эквивалентны.
Открыл новую issue с моими пояснениями, посмотрим, дойдёт или нет...
⸻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31❤6🔥3😐3💯2
Пых
Rector, это не то же самое!!! Пошёл делать тикет и нашёл уже существующий с ровно такой же проблемой. И знаете, что там ответил мейнтейнер? That's expected, if the type not match, that means a design issue in the first place, if property is private and…
Ура, Томас (автор Rector) подтвердил наличие проблемы! Если кто-то хочет поконтрибьютить, появилась несложная задачка.
👍34🔥9😁2
Пых
Найдите ошибку в терминологии.
У меня самые умные подписчики в IT 💙 — нашли ошибку за секунды. Ну а для меня этот комментарий стал поводом разложить всё по полочкам.
DI — Dependency Injection
Подход, при котором код явно декларирует необходимые зависимости и получает их снаружи, а не создаёт, не хардкодит и не извлекает их сам.
Как видите, зависимостью может быть что угодно, не только интерфейс: строка, число, инстанс финального класса.
Здесь часто проводят параллель с принципом Голливуда («Don’t call us, we’ll call you»): не ищите зависимости, мы их для вас соберём и передадим.
Мне ещё нравится на пальцах объяснять так:
Зависимости можно передавать через конструктор, параметр метода, путём клонирования with-методом (поддерживается в Symfony и Thesis DIC) или через сеттер (не рекомендую, так как мутабельно и можно получить неполный стейт или проблемы похуже в асинхронном сетапе).
DIC — Dependency Injection Container
Сугубо опциональная вещь! Контейнер призван облегчить сборку графа зависимостей в крупном проекте, но никто не мешает собирать приложение руками:
Большинство современных библиотек написано в стиле DI, но не требует контейнера. В README как раз обычно и показывают, как всё собрать "на коленке".
DIP — Dependency Inversion Principle
Последний принцип из SOLID. Он призван снизить каплинг и повысить переиспользуемость кода за счёт опоры на абстракции.
Обратите внимание, что в оригинальной формулировке принципа не используется понятие «интерфейс»:
Robert C. Martin, «The Dependency Inversion Principle», C++ Report, 1996
В C++, о котором идёт речь в статье, не было отдельной языковой конструкции
В наш
Внезапно для работы с разными потоками нам не потребовался
Инверсия зависимостей здесь реализована не в нашем коде, а в PHP: контракт — PHP Streams API, конкретные врапперы —
Получается, «абстракция» не тождественна интерфейсу или абстрактному классу. Даже финальный класс может абстрагировать вызывающий код от деталей реализации, пока сохраняет публичный контракт.
И вот тут-то мы и нащупали причину, по которой интерфейс может быть избыточным:
Если тип сам выражает требуемую клиенту стабильную абстракцию и не протекает низкоуровневыми деталями — на него можно опереться напрямую.
⸻
🌿 Анонсировали доклады Данилы Щуцкого и Саши Макарова на Пыхнике!
DI — Dependency Injection
Подход, при котором код явно декларирует необходимые зависимости и получает их снаружи, а не создаёт, не хардкодит и не извлекает их сам.
function logToFile(string $message): void
{
// не DI
file_put_contents(__DIR__ . '/../app.log', $message, FILE_APPEND);
}
final class StreamLogger extends Psr\Log\AbstractLogger
{
public function __construct(
// DI
private string $stream,
) {}
// ...
}
Как видите, зависимостью может быть что угодно, не только интерфейс: строка, число, инстанс финального класса.
Здесь часто проводят параллель с принципом Голливуда («Don’t call us, we’ll call you»): не ищите зависимости, мы их для вас соберём и передадим.
Мне ещё нравится на пальцах объяснять так:
Привет, я A, моя компетенция — α. Чтобы выполнить α, мне потребуются: x, опционально y и кто-то, кто умеет β. Предоставите — всё сделаю. Нет — сорян.
Зависимости можно передавать через конструктор, параметр метода, путём клонирования with-методом (поддерживается в Symfony и Thesis DIC) или через сеттер (не рекомендую, так как мутабельно и можно получить неполный стейт или проблемы похуже в асинхронном сетапе).
DIC — Dependency Injection Container
Сугубо опциональная вещь! Контейнер призван облегчить сборку графа зависимостей в крупном проекте, но никто не мешает собирать приложение руками:
// код в стиле DI, но контейнера нет
$app = new App(
logger: new StreamLogger(__DIR__ . '/../app.log'),
);
Большинство современных библиотек написано в стиле DI, но не требует контейнера. В README как раз обычно и показывают, как всё собрать "на коленке".
DIP — Dependency Inversion Principle
Последний принцип из SOLID. Он призван снизить каплинг и повысить переиспользуемость кода за счёт опоры на абстракции.
Обратите внимание, что в оригинальной формулировке принципа не используется понятие «интерфейс»:
A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
Robert C. Martin, «The Dependency Inversion Principle», C++ Report, 1996
В C++, о котором идёт речь в статье, не было отдельной языковой конструкции
interface: Мартин выражал объектные интерфейсы чисто абстрактными классами. Но в той же статье он отдельно приводит stdio.h как пример Dependency Inversion без всяких классов и виртуальных методов.В наш
StreamLogger тоже можно передать не только путь к локальному файлу:
$stdOutLogger = new StreamLogger('php://stdout');
$compressedLogger = new StreamLogger('compress.zlib:///var/log/app.log.gz');
$remoteLogger = new StreamLogger('ftp://logger:secret@logs.example.com/app.log');
Внезапно для работы с разными потоками нам не потребовался
interface! Строковый URI выбирает конкретную реализацию, а StreamLogger работает через общую абстракцию PHP-стримов.Инверсия зависимостей здесь реализована не в нашем коде, а в PHP: контракт — PHP Streams API, конкретные врапперы —
file, php, compress.zlib, ftp и пользовательские протоколы. StreamLogger пользуется этим контрактом, не зная, какая реализация скрывается за URI.Получается, «абстракция» не тождественна интерфейсу или абстрактному классу. Даже финальный класс может абстрагировать вызывающий код от деталей реализации, пока сохраняет публичный контракт.
И вот тут-то мы и нащупали причину, по которой интерфейс может быть избыточным:
Если тип сам выражает требуемую клиенту стабильную абстракцию и не протекает низкоуровневыми деталями — на него можно опереться напрямую.
⸻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍29❤10🔥6💩3
Пых
Ура, Томас (автор Rector) подтвердил наличие проблемы! Если кто-то хочет поконтрибьютить, появилась несложная задачка.
Одной проблемой в Rector меньше
Подписчик Илья Манютин зарешал тикет, который я на прошлой неделе открыл в Rector.
Спасибо огромное за потраченное время и токены! После релиза с удовольствием уберу
Подписчик Илья Манютин зарешал тикет, который я на прошлой неделе открыл в Rector.
Спасибо огромное за потраченное время и токены! После релиза с удовольствием уберу
ClassPropertyAssignToConstructorPromotionRector из skip.GitHub
[Php80] Skip promotion when property type is wider than constructor param on ClassPropertyAssignToConstructorPromotionRector by…
Fixes rectorphp/rector#9809
Summary
ClassPropertyAssignToConstructorPromotionRector no longer promotes when the property type is strictly wider than the constructor parameter type (e.g. ?object pro...
Summary
ClassPropertyAssignToConstructorPromotionRector no longer promotes when the property type is strictly wider than the constructor parameter type (e.g. ?object pro...
😁19👍16🔥7❤4
Forwarded from Пыхник’26 — PHP на природе
Программа Пыхника’26
У нас сразу три отличные новости!
🔥 Мы расширили программу: вместо запланированных восьми секций на Пыхнике’26 будет одиннадцать. Все доклады из шорт-листа оказались слишком крутыми, и мы решили никого не вычёркивать. Цена билета та же.
🥳 Онлайн-доклады пройдут в прямом эфире с 7 по 9 сентября. Пыхник’26 теперь не однодневное мероприятие, а целая PHP-неделя, которая завершится большой офлайн-встречей 11 сентября в Art Village.
🤩 Первый онлайн-доклад Дмитрия Dantes будет открытым! Посмотреть его смогут все желающие.
7 сентября📹
• Компилируемый PHP: перспективы и возможности — Дмитрий Dantes
8 сентября📹
• AI-first архитектура PHP-приложений — Дмитрий Кириллов
• Возвращаем gRPC в PHP — Вадим Занфир
9 сентября📹
• Testo: тестирование со вкусом хинкали — Алексей Гагарин
11 сентября🏠
• Request-Reply без ожидания и блокировок — Валентин Удальцов
• От скучной генерации к инженерии с ИИ — Данил Щуцкий
• Ломаем PHP-системы, чтобы они не падали — Маргарита Моногарова
• PHP в бинарнике на примере YiiPress — Александр Макаров
• Архитектура интеграций в PHP: как пережить чужие API — Олег Мифле
• AI-трансформация в PHP-команде: куда переехал bottleneck — Денис Кукуреко
• Флипчарт-сессия «Архитектурный экстремизм»
Программа готова — дальше дело за вами: https://planeta.ru/campaigns/pyhnik26
Билет теперь можно оплатить от компании — присылайте на conf@phpyh.ru тип, количество, реквизиты.
У нас сразу три отличные новости!
7 сентября
• Компилируемый PHP: перспективы и возможности — Дмитрий Dantes
8 сентября
• AI-first архитектура PHP-приложений — Дмитрий Кириллов
• Возвращаем gRPC в PHP — Вадим Занфир
9 сентября
• Testo: тестирование со вкусом хинкали — Алексей Гагарин
11 сентября
• Request-Reply без ожидания и блокировок — Валентин Удальцов
• От скучной генерации к инженерии с ИИ — Данил Щуцкий
• Ломаем PHP-системы, чтобы они не падали — Маргарита Моногарова
• PHP в бинарнике на примере YiiPress — Александр Макаров
• Архитектура интеграций в PHP: как пережить чужие API — Олег Мифле
• AI-трансформация в PHP-команде: куда переехал bottleneck — Денис Кукуреко
• Флипчарт-сессия «Архитектурный экстремизм»
Программа готова — дальше дело за вами: https://planeta.ru/campaigns/pyhnik26
Билет теперь можно оплатить от компании — присылайте на conf@phpyh.ru тип, количество, реквизиты.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25❤7 7👍5💊2🤔1
Уставший техдир
Цените какое золото! Процитирую перевод: Пытаюсь использовать Claude, чтобы он помогал мне писать код, но мне просто некомфортно позволять ему редактировать мои файлы. Кто-нибудь ещё так себя чувствует? Если я несу ответственность за код, мне НЕОБХОДИМО его…
Media is too big
VIEW IN TELEGRAM
Мои размышления про сложности перехода к агентскому кодингу в ответ на пост Глеба Михеева.
1👍80❤43💯13 6😢4🔥3🦄1