Пых

Пых

@phpyh

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

8 126подписчиков
Несколько раз в неделю🇷🇺

Похожие каналы

Все →

Последние посты

Пыхник’26Привет, дорогие Пыхари!На закрытии Пых.конф’25 мы с вами договорились, что встретимся снова в 2026. В этом году вместо полноценной Пых.конф мы решили попробовать другой формат — Пыхник!Примерный план:• конец августа / начало сентября;• загородный отель Art Village (12 км от МКАД);• 100-120 офлайн-участников;• 1 зал без параллельных потоков;• 8 лучших докладов про PHP и экосистему;• фуршет от конференции + рестораны на территории;• записи докладов для всех участников уже на следующий день;• возможность приехать с семьёй и провести в отеле ещё пару дней.По предварительным расчётам стоимость участия может составить 6000₽. Добиться такой цены получится за счёт простого формата: без нескольких залов, сложного продакшена и прочих атрибутов больших конференций.Чтобы не рисковать деньгами и понять, нужна ли такая встреча сообществу, мы думаем запустить краудфандинг на Planeta.ru (у нас уже был положительный опыт со слониками). Собираем необходимую сумму — проводим мероприятие, нет — деньги возвращаются.Пожалуйста, проголосуй ниже безотносительно даты и программы — так мы сможем предварительно оценить интерес. И ждём побольше вопросов в комментариях, чтобы мы ничего не забыли учесть!

15 июн. 2026 г.2 290В Telegram

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.⸻Этот пост я разместил

1 июн. 2026 г.4 430В Telegram

Горю с 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.

19 мая 2026 г.6 840В Telegram

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.

14 мая 2026 г.6 590В Telegram

Хроники пакета 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⸻Этот пост я раз

6 апр. 2026 г.8 910В Telegram
Пых — пост в ТГ канале

Прошло ровно полгода с Пых.конф’25 — что это значит?Это значит, что записи докладов теперь доступны каждому!Смотреть 28 крутых докладов на 📹 YouTube, 📹 VK Видео.

19 мар. 2026 г.7 670В Telegram
Пых — пост в ТГ канале

Пересоздал, залетайте! 📹 YouTube 📹 VK ВидеоЭто победа!!! 🎉https://github.com/Kenny1911/php-clone-with/pull/1https://github.com/vudaltsov/kenny-clone-with/actions/runs/22783907411

6 мар. 2026 г.10 400В Telegram

Пишем "полифил" для clone() из PHP 8.5 Open Source Цех #6 Залетайте! На стриме: • внимательно изучим RFC: Clone with v2, • склонируем kenny1911/php-clone-with, • подтянем агентом Codex phpt тесты из php-src, • доведём либу до состояния полифила (в кавычках…Пересоздал, залетайте!📹 YouTube 📹 VK Видео

6 мар. 2026 г.7 970В Telegram

Оказалось, что на сегодняшний вечер уже был запланирован стрим Кирилла Несмеянова в PHPeople — по слухам, он будет линчевать компоненты Thesis! Тогда встречаемся в Цеху завтра, в пятницу, в 19:00. А пока ловите мой пост про типы, тоже, кстати, из 🐘 PHPeople:Пишем "полифил" для clone() из PHP 8.5Open Source Цех #6Залетайте!На стриме:• внимательно изучим RFC: Clone with v2,• склонируем kenny1911/php-clone-with,• подтянем агентом Codex phpt тесты из php-src,• доведём либу до состояния полифила (в кавычках, потому что назвать функцию clone не получится).📹 YouTube 📹 VK Видео

6 мар. 2026 г.7 080В Telegram

Опускаем возвращаемые типыВ некоторых функциональных языках (Haskell, OCaml) можно явно не декларировать типы. Они реализуют вывод типов на базе алгоритма Хиндли-Милнера.Простой пример:fun a b -> a + bПроанализировав эту функцию, мы можем заключить, что в неё можно передавать только пары типов, для которых определён оператор +, например, числа. Возвращаемый тип для конкретной пары уже тривиально выводится.В таких языках типы обычно указывают только в верхнеуровневых функциях, чтобы избежать неоднозначности и зафиксировать API.Однако, Хиндли-Милнер очень плохо дружит с подтипизацией и юнионами, поэтому в условных Scala и TypeScript нельзя опускать типы параметров (интересный тикет для TS, кстати). А вот возвращаемые типы почти всегда можно:def twss(message: String) = message + " That's what she said!"twss("The box is too big to fit in the hole") // Stringfunction twss(message: string){ return message + " That's what she said!"}twss("The box is too big to fit in the hole") // stringЭто интуитивно понятно: когда мы знаем типы аргументов и умеем выводить типы выражений и инструкций, мы элементарно можем вывести возвращаемый тип, проанализировав все ветви выполнения.Теперь к PHP. Наши статанализаторы, грубо говоря, работают в двух режимах:• рефлектор — читает сигнатуру, но не смотрит внутрь функции — полезен для стабов и вендорного кода,• чекер — читает сигнатуру и проверяет "кишки" — используется для кода проекта.Тут важно, что в режиме чекера анализатор не запоминает выведенный из тела функции возвращаемый тип. В общем случае это ок, потому что публичные контракты должны быть задекларированы явно, но для приватных методов было бы здорово запоминать выведенный тип:final readonly class App{ // Валентин: PHPStan, выведи и запомни тут тип по-братски! private function twss(string $message) { return $message . " That's what she said!"; } public function greet(string $message): string { // PHPStan: Иди на х*й, Валентин! // Method App::g

5 мар. 2026 г.5 580В Telegram

Open Source Цех #6 К сожалению, сегодня не получится постримить. 😭 Но я жду всех завтра в 19:00 в «Цехе»! Доработаем библиотеку подписчика kenny1911/php-clone-with, чтобы она работала как clone() в PHP 8.5. 📹 YouTube 📹 VK Видео #open_source_цехОказалось, что на сегодняшний вечер уже был запланирован стрим Кирилла Несмеянова в PHPeople — по слухам, он будет линчевать компоненты Thesis!Тогда встречаемся в Цеху завтра, в пятницу, в 19:00. А пока ловите мой пост про типы, тоже, кстати, из 🐘 PHPeople:

5 мар. 2026 г.4 880В Telegram

Open Source Цех #6К сожалению, сегодня не получится постримить. 😭Но я жду всех завтра в 19:00 в «Цехе»! Доработаем библиотеку подписчика kenny1911/php-clone-with, чтобы она работала как clone() в PHP 8.5.📹 YouTube 📹 VK Видео#open_source_цех

4 мар. 2026 г.5 500В Telegram

Типабезопасный код — когда разработчик подавляет замечания статанализатора. #пыхословТем временем в php-src пару часов назад подъехал PR с реифицированными дженериками от аккаунта, зарегистрированного на GitHub сегодня...Кто-то явно наслопал. 😵‍💫https://github.com/php/php-src/pull/21317https://www.reddit.com/r/PHP/comments/1rharkk/someone_just_created_pr_with_fully_working/

28 февр. 2026 г.7 530В Telegram
Пых — пост в ТГ канале

Типабезопасный код — когда разработчик подавляет замечания статанализатора.#пыхослов

28 февр. 2026 г.7 460В Telegram

PHP-линч #32Через 10 минут начинаем, пишите, что хотите сегодня посмотреть!📹 YouTube 📹 VK Видео#php_линч

25 февр. 2026 г.7 310В Telegram