TGViewer
Channel Public Channel
Пых

Пых

@phpyh

Блог Валентина Удальцова о разработке на PHP.

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

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

Для связи используйте личные сообщения канала.
Subscribers
7.87K
Photos
285
Videos
20
Links
627

Showing posts older than #972 · Back to latest

Older Posts 20 shown
Post #971 4.04K
Найдите ошибку в терминологии.
  • 🤔 14
  • 😁 9
  • 👍 1
  • 💯 1
Post #970 6.49K
Пых 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
Post #969 5.6K
Rector, это не то же самое!!!

Пошёл делать тикет и нашёл уже существующий с ровно такой же проблемой.

И знаете, что там ответил мейнтейнер?

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 с моими пояснениями, посмотрим, дойдёт или нет...

🌿 Приняли первый доклад на Пыхник’26!
  • 👍 32
  • ❤ 6
  • 🔥 3
  • 😐 3
  • 💯 2
Post #968 5.26K
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());


Короче, как вам такой стиль кодирования?
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.
  • 👎 79
  • 🥴 27
  • 👍 8
  • 🤯 5
  • 🤮 5
  • 🔥 2
  • ❤ 1
  • 🙏 1
Post #967 5.13K
Эквайринг Т-Банка, 2026 год

Повторное выставление счёта (ручка Init) бросает ошибку о том, что такой заказ уже есть, вместо того, чтобы идемпотентно вернуть ссылку на оплату (PaymentURL).

Окей, допустим. План Б: оборачиваю Init в try/catch и при ошибке о дублирующемся заказе пробую GetState и CheckOrder, которые возвращают инфу о выставленном счёте.

Но ни в одном из них нет PaymentURL!!! 🤬

То есть у меня есть лишь один шанс записать PaymentURL после получения ответа от Т-Банка. А если ответ не дошёл по сети? А если мой сервер упал? А если БД нагружена? А если кролик отошёл погрызть морковку?

Ну как так-то нахуй...

Решение, конечно, есть: при ретрае и ошибке о дубле создавать новый заказ с другим ID. Но это означает, что я уже не могу без таблички скоррелировать ID своего инвойса с ID, который использую для заказа на их стороне. А это дополнительное I/O на пустом месте, брошенные заказы в админке Т-Банка и усложнённое расследование инцидентов.

Если у кого-то есть друзья в Т-Банке, попросите исправить это недоразумение.
  • 🤣 57
  • 👍 12
  • 🤡 10
  • ❤ 5
  • 🔥 4
  • 😡 4
  • 🤔 1
  • 🎉 1
  • 🤮 1
  • 💯 1
Post #966 5.02K
Пых Video message
Про кодогенерируемые контейнеры.
  • 💩 6
  • 🔥 4
Post #965 5.16K

This post (sticker, poll or similar) has no web preview. Open in Telegram

  • 👍 18
  • 🔥 7
  • 💯 5
  • 💩 3
  • ❤ 2
Post #964 5.58K
Пых 🍆 Thesis Dic Прошло полтора года с тех пор, как я всерьёз начал пилить DI контейнер для Thesis. Набор требований у меня был большой: Модульность Всё есть модуль, включая само приложение. PHP DSL Выразительный и лаконичный, не на массивах. Типобезопасность…
Первый бенчмарк thesis/dic

Смоделировал следующий сценарий работы неумирающего приложения:

Задепоили приложение в рантайме а-ля RoadRunner, собрали контейнер и готовы принимать запросы. Ни один сервис не инстанциирован, но контейнер максимально готов.

Как быстро испытуемые вернут инстанс контроллера с несколькими зависимостями?


Сравнение, конечно, не совсем равнозначное, потому что только у Thesis есть этап диспоуза сервисов после скоупа и не во всех контейнерах есть полноценные scoped сервисы.

Но в целом результат ожидаемый. Скомпилированный контейнер Symfony почти эквалентен написанному руками коду (manual).

Thesis (current) быстрее Laravel и Yii3, потому что рефлексию и резолвинг графа зависимостей он делает на этапе сборки, а в рантайме вызывает полностью готовые фабрики.

Для бенчмарка попробовал #[Bench] в 🐘 Testo.

Код скину, когда доделаю ещё несколько важных сценариев. Накидывайте идей.
  • 🔥 24
  • 👍 9
  • 👏 4
  • ❤ 1
Post #963 4.44K

Forwarded from Пыхник’26 онлайн | 28 сентября – 2 октября

Запустили краудфандинг!!!

11 сентября в Art Village под Москвой собираем небольшую PHP-конференцию на 120 участников: один поток, 8 докладов и много живого общения.

Средства на мероприятие собираем через краудфандинг. Можно взять офлайн-билет, доступ к записям, футболку «Вы вымрете, а я останусь» или просто поддержать проект любой суммой.

https://planeta.ru/campaigns/pyhnik26
  • 👍 23
  • ❤ 11
  • 🔥 9
  • 💩 9
Post #960 5.42K
🍆 Thesis Dic

Прошло полтора года с тех пор, как я всерьёз начал пилить 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(),
));
  • 🔥 38
  • ❤ 7
  • 👍 7
  • 🤔 3
  • 😱 1
  • 💩 1
  • 😭 1
Post #959 5.22K
Пыхник’26 онлайн | 28 сентября – 2 октября Video message
Скидывайте идеи футболок под этим сообщением.👇
  • 🖕 8
  • 👍 2
Post #956 6.33K

This post (sticker, poll or similar) has no web preview. Open in Telegram

  • 😁 8
  • 👍 3
Post #954 6.41K

Forwarded from Пыхник’26 онлайн | 28 сентября – 2 октября

Пыхник’26

Привет, дорогие Пыхари!

На закрытии Пых.конф’25 мы с вами договорились, что встретимся снова в 2026. В этом году вместо полноценной Пых.конф мы решили попробовать другой формат — Пыхник!

Примерный план:
• конец августа / начало сентября;
• загородный отель Art Village (12 км от МКАД);
• 100-120 офлайн-участников;
• 1 зал без параллельных потоков;
• 8 лучших докладов про PHP и экосистему;
• фуршет от конференции + рестораны на территории;
• записи докладов для всех участников уже на следующий день;
• возможность приехать с семьёй и провести в отеле ещё пару дней.

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

Чтобы не рисковать деньгами и понять, нужна ли такая встреча сообществу, мы думаем запустить краудфандинг на Planeta.ru (у нас уже был положительный опыт со слониками). Собираем необходимую сумму — проводим мероприятие, нет — деньги возвращаются.

Пожалуйста, проголосуй ниже безотносительно даты и программы — так мы сможем предварительно оценить интерес. И ждём побольше вопросов в комментариях, чтобы мы ничего не забыли учесть!
  • 🔥 23
  • 👍 7
  • ❤ 6
  • 😁 1
  • 💩 1
  • 🙏 1
Post #953 6.87K

This post (sticker, poll or similar) has no web preview. Open in Telegram

  • 🔥 82
  • ❤ 17
  • 👍 16
  • 💩 7
  • 😁 3
  • 🙏 1
Post #952 6.66K
Whole Value a.k.a. Value Object

Я сейчас работаю над кодом, которому для работы требуется список параметров функции:


/**
* @param list<ReflectionParameter> $parameters
*/
function make(array $parameters): Something {}


На первый взгляд, всё здорово. list<ReflectionParameter> выражает минимально необходимое знание для решения задачи да и вообще это список из объектов-значений, а не каких-то там примитивов.

Но есть нюанс.

В такой make можно передать невозможный список параметров, например:


$trimReflection = new ReflectionFunction(trim(...));

make(array_reverse($trimReflection->getParameters()));


С точки зрения типов всё верно, но инвариант "опциональные параметры идут после обязательных" в переданном списке нарушен, что может привести к неправильной работе функции. Можно передать параметры от разных функций (тогда $parameters[$i]->getDeclaringFunction() будет давать неконсистентный результат), можно поставить вариадик в начало — способов задать неверный список много, потому что list<ReflectionParameter> ничего толком не регламентирует.

Если функция работает с концепцией "список параметров валидной сигнатуры", она должна принимать ReflectionFunctionAbstract и сама вызывать getParameters():


function make(ReflectionFunctionAbstract $function): Something
{
$parameters = $function->getParameters();

// ...
}


Теперь в теле make можно быть уверенным, что список параметров удовлетворяет всем инвариантам, ведь отрефлексировать функцию с неверной сигнатурой не получится...

Хотя подождите... Можно же так поломать:


final class MessedReflectionFunction extends ReflectionFunction
{
public function getParameters(): array
{
return array_reverse(parent::getParameters());
}
}

make(new MessedReflectionFunction(trim(...)));


Но это уже разговор про LSP и что наследники не должны нарушать инварианты родителей — тема для другого поста. 😊

Мораль такая: принимайте не просто минимум знаний, а минимум, гарантирующий необходимые инварианты. Это и есть концепция Whole Value.

Этот пост я разместил неделю назад в 🐘 PHPeople.
  • 👍 23
  • ❤ 9
  • 🥴 6
  • 🤯 5
  • 🔥 4
  • 👎 1
  • 👏 1
  • 🤔 1
Post #951 6.97K

This post (sticker, poll or similar) has no web preview. Open in Telegram

  • 🙈 32
  • 😱 11
  • 🔥 5
  • 🌭 2
  • ❤ 1
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →