uk
Feedback
Пых

Пых

Відкрити в Telegram

Блог Валентина Удальцова о разработке на PHP. Хобот @phpyhobot https://youtube.com/@phpyh https://vkvideo.ru/@phpyh https://t.me/isPHPdying Статистика: https://t.me/INOTAROBOT?start=st1219340804 Для связи используйте личные сообщения канала.

Показати більше
8 100
Підписники
-324 години
-247 днів
-730 день

Триває завантаження даних...

Залучення підписників
червень '26
червень '26
+106
в 0 каналах
травень '26
+141
в 0 каналах
Get PRO
квітень '26
+66
в 0 каналах
Get PRO
березень '26
+72
в 1 каналах
Get PRO
лютий '26
+92
в 3 каналах
Get PRO
січень '26
+83
в 2 каналах
Get PRO
грудень '25
+69
в 3 каналах
Get PRO
листопад '25
+89
в 0 каналах
Get PRO
жовтень '25
+90
в 1 каналах
Get PRO
вересень '25
+131
в 3 каналах
Get PRO
серпень '25
+162
в 4 каналах
Get PRO
липень '25
+161
в 3 каналах
Get PRO
червень '25
+144
в 17 каналах
Get PRO
травень '25
+143
в 2 каналах
Get PRO
квітень '25
+138
в 3 каналах
Get PRO
березень '25
+125
в 3 каналах
Get PRO
лютий '25
+238
в 5 каналах
Get PRO
січень '25
+177
в 6 каналах
Get PRO
грудень '24
+235
в 1 каналах
Get PRO
листопад '24
+383
в 5 каналах
Get PRO
жовтень '24
+269
в 4 каналах
Get PRO
вересень '24
+103
в 1 каналах
Get PRO
серпень '24
+132
в 2 каналах
Get PRO
липень '24
+89
в 0 каналах
Get PRO
червень '24
+106
в 4 каналах
Get PRO
травень '24
+177
в 2 каналах
Get PRO
квітень '24
+266
в 0 каналах
Get PRO
березень '24
+365
в 1 каналах
Get PRO
лютий '24
+658
в 0 каналах
Get PRO
січень '24
+352
в 5 каналах
Get PRO
грудень '23
+274
в 3 каналах
Get PRO
листопад '23
+140
в 3 каналах
Get PRO
жовтень '23
+121
в 1 каналах
Get PRO
вересень '23
+191
в 0 каналах
Get PRO
серпень '23
+354
в 0 каналах
Get PRO
липень '23
+328
в 0 каналах
Get PRO
червень '23
+354
в 0 каналах
Get PRO
травень '23
+493
в 0 каналах
Get PRO
квітень '23
+290
в 0 каналах
Get PRO
березень '23
+689
в 0 каналах
Get PRO
лютий '23
+353
в 0 каналах
Get PRO
січень '23
+250
в 0 каналах
Get PRO
грудень '22
+217
в 0 каналах
Get PRO
листопад '22
+146
в 0 каналах
Get PRO
жовтень '22
+113
в 0 каналах
Get PRO
вересень '22
+74
в 0 каналах
Get PRO
серпень '22
+166
в 0 каналах
Get PRO
липень '22
+143
в 0 каналах
Get PRO
червень '22
+142
в 0 каналах
Get PRO
травень '22
+67
в 0 каналах
Get PRO
квітень '22
+54
в 0 каналах
Get PRO
березень '22
+57
в 0 каналах
Get PRO
лютий '22
+141
в 0 каналах
Get PRO
січень '22
+157
в 0 каналах
Get PRO
грудень '21
+181
в 0 каналах
Get PRO
листопад '21
+92
в 0 каналах
Get PRO
жовтень '21
+64
в 0 каналах
Get PRO
вересень '21
+47
в 0 каналах
Get PRO
серпень '21
+63
в 0 каналах
Get PRO
липень '21
+75
в 0 каналах
Get PRO
червень '21
+127
в 0 каналах
Get PRO
травень '21
+62
в 0 каналах
Get PRO
квітень '21
+113
в 0 каналах
Get PRO
березень '21
+238
в 0 каналах
Get PRO
лютий '21
+316
в 0 каналах
Get PRO
січень '21
+242
в 0 каналах
Get PRO
грудень '20
+3 333
в 0 каналах
Дата
Залучення підписників
Згадування
Канали
30 червня0
29 червня+1
28 червня+1
27 червня+4
26 червня0
25 червня0
24 червня0
23 червня+4
22 червня+4
21 червня+4
20 червня+3
19 червня+2
18 червня+7
17 червня+2
16 червня+1
15 червня+1
14 червня+8
13 червня+3
12 червня+8
11 червня0
10 червня+10
09 червня0
08 червня+4
07 червня+2
06 червня+6
05 червня+4
04 червня+7
03 червня+3
02 червня+7
01 червня+10
Дописи каналу
🍆 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(),
));

2
Скидывайте идеи футболок под этим сообщением.👇
2 313
3
Скидывайте идеи футболок под этим сообщением.👇
1
4
Відеоповідомлення
2 151
5
Какой стиль оформления значений в исключениях лучше читается?
3 417
6
Как тебе идея?
5 041
7
Пыхник’26 Привет, дорогие Пыхари! На закрытии Пых.конф’25 мы с вами договорились, что встретимся снова в 2026. В этом году вместо полноценной Пых.конф мы решили попробовать другой формат — Пыхник! Примерный план: • конец августа / начало сентября; • загородный отель Art Village (12 км от МКАД); • 100-120 офлайн-участников; • 1 зал без параллельных потоков; • 8 лучших докладов про PHP и экосистему; • фуршет от конференции + рестораны на территории; • записи докладов для всех участников уже на следующий день; • возможность приехать с семьёй и провести в отеле ещё пару дней. По предварительным расчётам стоимость участия может составить 6000₽. Добиться такой цены получится за счёт простого формата: без нескольких залов, сложного продакшена и прочих атрибутов больших конференций. Чтобы не рисковать деньгами и понять, нужна ли такая встреча сообществу, мы думаем запустить краудфандинг на Planeta.ru (у нас уже был положительный опыт со слониками). Собираем необходимую сумму — проводим мероприятие, нет — деньги возвращаются. Пожалуйста, проголосуй ниже безотносительно даты и программы — так мы сможем предварительно оценить интерес. И ждём побольше вопросов в комментариях, чтобы мы ничего не забыли учесть!
4 442
8
Відеоповідомлення
5 942
9
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.
5 348
10
Немає тексту...
5 709
11
Горю с PHPStan Все привыкли ограничивать дженерики типов сверху (upper bound): @template T of U. Но бывают ситуации, когда дженерик логично ограничить снизу (lower bound), в частности другим дженериком. Пример. Представьте, что существует ссылка на какое-то значение и нужно заапкастить (преобразовать в супертип) тип значения в этой ссылке: use Psr\Log\LoggerInterface as Logger; use Monolog\Logger as Monolog; use Typhoon\Type; /** * @template T */ interface Ref { /** * @template S, где S — супертип для T * @param Type<S> $super * @return self<S> */ public function as(Type $super): self; } /** * @param Ref<Monolog> $ref * @return Ref<Logger> */ function upcastLogger(Ref $ref): Ref { return $ref->as(Type\objectT(Logger::class)); } В Java wildcards это выражается так: ? super T. В Scala оператором: [S >: T]. В PHPStan, о чудо, тоже начали об этом думать аж в 2021 году: phpstan/phpstan/issues/5179. Я был в курсе, и, не глядя в issue, решил попробовать. Пишу: /** * @template S super T * @param Type<S> $super * @return self<S> */ public function as(Type $super): self; Анализатор говорит No issues!, я радостно продолжаю работать. А потом намеренно пробую вызвать $ref->as(Type\stringT) и всё равно вижу No issues! Разбираюсь и понимаю, что PHPStan поддержал пару лет назад синтаксис super, но только в PHPDoc-парсере! Исходный тикет в анализаторе всё ещё не закрыт! Представьте, что в PHP добавили property hooks только на уровне синтаксиса — по факту они вообще не работают. Как вам такая фича? Между PHP-анализатором и PHPDoc-парсером есть фундаментальный каплинг (то есть, кохижен). Нельзя поправить одно и не учесть это в другом. Нормально разделять компоненты и делать один более универсальным, но тогда должны быть фича-флаги, при помощи которых второй компонент выключает (opt-out) или не включает (opt-in) неподдерживаемое поведение в первом. ⸻ Этот пост я разместил неделю назад в 🐘 PHPeople.
6 818
12
base64url Хотел предложить base64url_encode(), base64url_decode() в Core — давно стандартизованная кодировка, используется в JWT, OAuth 2.0 и WebAuthn. Но потом нашёл в почте прошлогодний тред с куда более фундаментальным RFC: namespace Encoding; enum Base64 { case Standard; case UrlSafe; case Imap; } enum PaddingMode { case VariantControlled; case StripPadding; case PreservePadding; } enum DecodingMode { case Forgiving; case Strict; } enum TimingMode { case Variable; case Constant; } /** * @throws UnableToEncodeException */ function base64_encode( string $data, Base64 $variant = Base64::Standard, PaddingMode $paddingMode = PaddingMode::VariantControlled, TimingMode $timingMode = TimingMode::Variable, ): string; /** * @throws UnableToDecodeException */ function base64_decode( string $data, Base64 $variant = Base64::Standard, DecodingMode $decodingMode = DecodingMode::Strict, TimingMode $timingMode = TimingMode::Variable, ): string; Ну и аналогичные функции для base16, base32, base58 (Bitcoin) и base85 (PDF). https://wiki.php.net/rfc/data_encoding_api Спросил автора, не планирует ли он возобновить работу над RFC. Вот его ответ: Hi Valentin, We are still working on it with Tim Düsterhus doing the full implementation. Best regards Так что ждём! ⸻ Этот пост я разместил неделю назад в 🐘 PHPeople.
6 437
13
Используешь Dev Containers для локальной разработки?
0
14
Відеоповідомлення
0
15
Хроники пакета exceptionally Вы наверняка знаете проект thecodingmachine/safe. A set of core PHP functions rewritten to throw exceptions instead of returning false when an error is encountered. // вместо $contents = @file_get_contents($file); if ($contents === false) { throw new RuntimeException("File {$file} is not readable"); } // пишем $contents = Safe\file_get_contents($file); Такой подход мне всегда казался максимально бредовым: скодогенерировать обёртки над всеми стандартными PHP-функциями и написать плагины под каждый статанализатор вместо того, чтобы воспользоваться композицией функций. Поэтому ночью 3 августа 2019 года я написал библиотечку vudaltsov/exceptionally, которая решала проблему куда более элегантно: $contents = exceptionallyCall('file_get_contents', $file); Тогда ещё не было стрелочных функций и first class callable синтаксиса, поэтому краткая запись могла выглядеть только так. Забавно, что утром в тот же день в паре кварталов от моей квартиры загорелся склад, подробнее в личном канале. Через 2 дня я предложил RFC в Symfony, а ещё через неделю Yonel Ceruto добавил статический метод ErrorHandler::call(), он там есть и по сей день. Но настали времена, когда я больше не пишу на Symfony, а функция всё ещё актуальна. Поэтому я решил перенести vudaltsov/exceptionally в thesis/exceptionally, упростить и приправить дженериками: /** * @template T * @param callable(): (T|false) $function * @return T * @throws \ErrorException */ function exceptionally( callable $function, int $errorLevels = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED, ): mixed {} // PHP < 8.6 $contents = exceptionally(static fn () => file_get_contents($file)); // PHP >= 8.6 с partial function application $contents = exceptionally(file_get_contents($file, ...)); Исключение ErrorException, кстати, существует в ядре ровно для этой задачи, поэтому помимо стандартных параметров оно принимает файл и номер строки. Пользуйтесь Thesis Exceptionally на здоровье! composer require thesis/exceptionally ⸻ Этот пост я разместил неделю назад в 🐘 PHPeople. Кстати, общая группа там теперь бесплатная — подписывайся!
0