Telegram Web Link
Пых
BeerPHP сменил локацию! Митап пройдёт в лофте "Событие" на Таганке. Организаторы говорят, что эта площадка значительно лучше. Дата и время те же самые: 6 июня (завтра) в 19:00. Также мне разрешили поделиться с вами записью доклада после мероприятия! ht…
📹 BeerPHP. Пишем на PHP и не теряем память

Наконец-то мы смонтировали ролик! Помимо доклада в нём есть признание в любви к вам, матерные слова и подробная история моего RFC.

Получилось очень задорно! Спасибо организаторам и участникам BeerPHP за такую крутую атмосферу.

https://youtu.be/56I5C0NYjv8
https://vudaltsov.github.io/memory-leaks-slides/
Please open Telegram to view this post
VIEW IN TELEGRAM
🎵 Музыкальный стрим!

Хотите музыкальный стрим сегодня с 17 до 18 МСК? Позже, к сожалению, не получится из-за договорённостей с соседями. Сыграю несколько песен на барабанах (можно будет даже заказать), отвечу на ваши вопросы. Посмотрим, что из этого выйдет. 😉

Ставь 🤩, если заглянешь.
Please open Telegram to view this post
VIEW IN TELEGRAM
🎙 PHP Russia 2024 быть!

28 и 29 ноября в Москве пройдёт конференция Highload, в рамках которой 16 докладов будут выделены под PHP Russia.

Наилучший способ туда попасть — выступить! Плюшки спикера: куратор из программного комитета для подготовки крутого доклада (например, я), транспорт до Москвы и комфортное проживание, бейдж с полным доступом ко всем залам и зонам Highload. А ещё спикер навсегда вписывает себя и свои идеи в историю PHP. Короче, грех не податься!

Дай угадаю. Если ты раньше не выступал, то сейчас думаешь: "Ой, ну мне не о чем рассказывать, у нас всё стандартно." Поверь, так не бывает! У каждой компании есть ноу-хау, иначе она была бы неприбыльной и ты работал бы в другой. А раз "every company is a software company", то ноу-хау должно быть и в софте. Твоя задача — найти его и заполнить форму. Ещё можно предложить доклад про это самое "стандартно" и как его правильно готовить. Дальше мы уже решим, что впишется в программу, а что нет.

Итак, темы этого года:
• FFI, практическое применение
• AI/ML + PHP
• Производительность
• Devops под PHP
• Лучшие практики
• Новые крутые либы
• Альтернативные рантаймы
• Новые фреймворки
• Опыт больших сложных проектов на PHP

Ждём твой доклад по адресу https://cfp.phprussia.ru/ до 2-ого сентября.
Please open Telegram to view this post
VIEW IN TELEGRAM
Александр Кирсанов, руководитель команды KPHP, ушёл из ВК

https://vk.com/@kphp-all

Буду ждать от Саши новых проектов для PHP!

Ребят, я тут затайфунился жёстко. Скоро снова выйду в свет. Куча идей постов, стримов и, конечно, новый поток курса. Просто нужно ещё немного времени. Всех люблю!
💙 Typhoon 0.4.0

https://github.com/typhoon-php/typhoon/releases/tag/0.4.0

Вчера ночью, ровно через 5 месяцев после 0.3.0, поставил следующий минорный тег. По сути, он, конечно, мажорный, так как 0.y релизы могут ломать обратную совместимость, чем мы не преминули многократно воспользоваться. 😈 С этой версии будем вести Changelog, так как капитальных переписываний в ближайшее время не ожидается.

Это был долгий, но полный важных осознаний путь. В цикле постов расскажу про каждое.
Please open Telegram to view this post
VIEW IN TELEGRAM
Пых
💙 Typhoon 0.4.0 https://github.com/typhoon-php/typhoon/releases/tag/0.4.0 Вчера ночью, ровно через 5 месяцев после 0.3.0, поставил следующий минорный тег. По сути, он, конечно, мажорный, так как 0.y релизы могут ломать обратную совместимость, чем мы не преминули…
💙 Typhoon 0.4. Тип not

Как вы наверняка знаете, в Psalm и PHPStan есть типы non-empty-string, non-empty-list<TValue> и non-empty-array<TKey, TValue>. В далёкой 0.2 версии тайфуна они обрабатывались индивидуально — у каждого был свой метод в TypeVisitor. Затем в 0.3 мы попытались сократить избыточность, внедрив конструктор non-empty<T>, но не покидало ощущение, что это костыль. И, наконец, месяц назад пришло осознание, что нужен конструктор не для непустоты, а для отрицания.

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


/**
* @template T
* @param T $bar
*/
function foo(mixed $bar): void
{
if (is_string($bar)) {
// тут у $bar тип T & string

return;
}

// а тут у $bar тип T & !string

trim($bar); // ошибка: ожидается string
}


Очевидно, что для статического анализатора отрицание может быть очень полезно. В TypeScript был такой PR, но почему-то заглох. Psalm и PHPStan частично поддерживают ! в аннотациях @assert:


/**
* @psalm-assert !null $value
* @phpstan-assert !null $value
*/
function assertNotNull($value): void
{
if ($value === null) {
throw new InvalidArgumentException();
}
}


Ну а в typhoon/type 0.4 not теперь first-class тип. Семейство non-empty-*, а заодно и non-falsy-string выражаются так:


non-empty-string = string & !''
non-falsy-string = truthy-string = non-empty-string & !'0'
non-empty-list<TValue> = list<TValue> & !array{}
non-empty-array<TKey, TValue> = array<TKey, TValue> & !array{}

Здесь array{} — это запечатанный array-shape без элементов, то есть []. Его ещё часто пишут как array<never, never>.


Чтобы обойти types::nonEmptyString через TypeVisitor, достаточно смаршрутизировать его как пересечение с соответствующими аргументами:


enum types implements Type
{
// ...
case nonEmptyString;

public function accept(TypeVisitor $visitor): mixed
{
return match ($this) {
// ...
self::nonEmptyString => $visitor->intersection($this, [
self::string,
self::not(self::string('')),
]),
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
Пых
💙 Typhoon 0.4.0 https://github.com/typhoon-php/typhoon/releases/tag/0.4.0 Вчера ночью, ровно через 5 месяцев после 0.3.0, поставил следующий минорный тег. По сути, он, конечно, мажорный, так как 0.y релизы могут ломать обратную совместимость, чем мы не преминули…
💙 Typhoon 0.4. self, parent, static

В документации PHP self, parent и static называются относительными типами классов (relative class types). С этой троицей мы боремся с самого начала. Каждый релиз — новый раунд.

Раунд 1

Возьмём пару классов:


class A extends stdClass
{
public function self(): self { return $this; }
public function parent(): parent { return $this; }
public function static(): static { return $this; }
}

final class B extends A {}


С self и parent здесь всё просто и однозначно: в классе A self = A, parent = stdClass, в класс B копируем эти 2 метода с уже разрешёнными типами A и stdClass. Значит, self и parent можно не оформлять как самостоятельные типы, а сразу заменять на конкретные классы из скоупа.

Можем ли мы так же разрешить static? Нет, так как это имя не текущего, а вызываемого класса (см. позднее статическое связывание): в A static резолвится как A, а в B — как B. При построении рефлексии мы должны в каждом унаследованном методе обновить static с учётом текущего скоупа. Для этого замоделируем static как first-class тип, который знает, где он сейчас находится, и получим typhoon/type 0.2:


A,B::self() возвращают types::object(A::class)
A,B::parent() возвращают types::object(stdClass::class)
A::static() возвращает types::static(A::class)
B::static() возвращает types::static(B::class)
Please open Telegram to view this post
VIEW IN TELEGRAM
Пых
💙 Typhoon 0.4. self, parent, static В документации PHP self, parent и static называются относительными типами классов (relative class types). С этой троицей мы боремся с самого начала. Каждый релиз — новый раунд. Раунд 1 Возьмём пару классов: class A…
💙 Typhoon 0.4. self, parent, static.

В Typhoon 0.3 я начал добавлять поддержку трейтов. И всё, естественно, поломалось. 🙈

Раунд 2


trait T
{
public function self(): self { return $this; }
public function parent(): parent { return $this; }
public function static(): static { return $this; }
}


Валидный ли это код? Да. Вот только ни один из трёх типов невозможно отрезолвить так же, как мы это делали выше. self и parent не на что заменить, так как трейт сам по себе не является типом и не может наследовать другие. Для static отсутствует скоуп-класс.

Нативная рефлексия, кстати, вообще не парится: всегда возвращает ReflectionNamedType с 'self', 'parent' или 'static'. Мы так делать не хотим, потому что это просто откладывание проблемы на потом.

Постепенно приходит понимание, что в трейтах относительные типы работают как плейсхолдеры. В Psalm и PHPStan их даже можно ограничивать через аннотации @require-extends и @require-implements. Всё это напоминает... дженерики!

Действительно,


/**
* @psalm-require-extends stdClass
* @phpstan-require-extends stdClass
*/
trait T
{
public function self(): self { return $this; }
public function parent(): parent { return $this; }
public function static(): static { return $this; }
}

final class A extends stdClass
{
use T;
}


можно с натяжкой переписать как


/**
* @template self of object
* @template parent of stdClass
* @template static of self
*/
trait T
{
public function self(): self { return $this; }
public function parent(): parent { return $this; }
public function static(): static { return $this; }
}

final class A extends stdClass
{
/**
* @use T<self, parent, self>
*/
use T;
}


Выглядит логично, но кривовато, потому что в реальности это не объявленные пользователем дженерики, а какие-то встроенные, да ещё и доступные в статике. Тем не менее, на тот момент идея мне очень нравилась, и, подставив пару костылей, я её реализовал в typhoon 0.3 и даже гордо рассказал про это на Стачке:


types::template('self', types::atClass(T::class))
types::template('parent', types::atClass(T::class))
types::template('static', types::atClass(T::class))
Please open Telegram to view this post
VIEW IN TELEGRAM
Пых
💙 Typhoon 0.4. self, parent, static. В Typhoon 0.3 я начал добавлять поддержку трейтов. И всё, естественно, поломалось. 🙈 Раунд 2 trait T { public function self(): self { return $this; } public function parent(): parent { return $this; } public…
💙 Typhoon 0.4. self, parent, static

Есть у меня одна черта: я периодически возвращаюсь ко всем своим костылям, пока не сделаю нормально или не уволюсь. О том, что для относительных типов не стоит использовать дженерики, кричали разные куски кода, но всегда казалось, что будет слишком жирно делать их first-class. А потом я задумался над тем, как работают с этими типами анонимные функции.

Раунд 3


final class A extends ArrayObject
{
public function self() { return fn (): self => new self(); }
public function parent() { return fn (): parent => new parent(); }
public function static() { return fn (): static => new static(); }
}

final class B extends stdClass {}

$self = (new A())->self();
echo $self()::class; // A
echo $self->call(new B)::class; // B

$parent = (new A())->parent();
echo $parent()::class; // ArrayObject
echo $parent->call(new B)::class; // stdClass

$static = (new A())->static();
echo $static()::class; // A
echo $static->call(new B)::class; // B


Получается, что self, parent и static отлично себя чувствуют в анонимных функциях и, как и в трейтах, резолвятся в зависимости от скоупа, в том числе поддерживают его изменение в рантайме. Посмотрев на этот сниппет, мы наконец-то согласились, что относительные типы класса везде в PHP ведут себя логично и единообразно и должны быть замоделированы как first-class типы.

Теперь в typhoon/type 0.4 можно создавать self, parent и static с классом скоупа и без него. Последнее как раз требуется в трейтах и непривязанных анонимных функциях. В TypeVisitor каждый относительный тип обрабатывается индивидуально. Ну и, конечно, поддерживаются аргументы типов (а-ля self<X, Y>). Комбо-пример:


trait T
{
/**
* @return array{self, parent, static}
*/
public function types(): array
{
return [$this, $this, $this];
}
}

abstract class A extends stdClass
{
use T;
}

final class B extends A {}

$type = TyphoonReflector::build()
->reflectClass(B::class)
->methods()['types']
->returnType();

echo stringify($type);

// array{self@A, parent@stdClass, static@B}
Please open Telegram to view this post
VIEW IN TELEGRAM
👨‍🏫 Хардкорный курс PHP, 4-й поток

Традиционно после 0.X релиза Typhoon я запускаю X-й поток курса! Сегодня X = 4.

Набор на четвёртый поток будет осуществляться как обычно. Завтра (в среду, 7 августа) в 15:00 по Москве на канале Пых появится ссылка на срез знаний. Необходимо его пройти, чтобы попасть на курс. При проверке я сначала отберу анкеты с правильными ответами, а затем отранжирую их по времени. Удачи!

Обновлённая страничка курса: hardcorephp.ru
Please open Telegram to view this post
VIEW IN TELEGRAM
Пых
👨‍🏫 Срез знаний для 4-го потока Хардкорного курса PHP! https://forms.gle/PFvHT7vujHEPuSs29 Удачи! 😉️️️️️️
🤨 Задачка со звёздочкой

Теперь, когда первая сотня абитуриентов прислала анкеты, предлагаю всем подумать над внеконкурсным заданием. Решите 5-ю задачу с теми же вводными в обратную сторону. То есть у вас должен получиться класс, который, наоборот, позволит использовать Psr\Middleware как Symfony\Subscriber. Идеи можно обсуждать в комментариях к этому посту. Позже всё разберём на стриме.

Gist с полной формулировкой этого варианта задачи: https://gist.github.com/vudaltsov/dc4f372692d2eabbc8c3d29cd4de0ccd

Навеяно, кстати, весьма драматическими событиями. В 2018-м году сообщество Symfony активно обсуждало невыполнение PHP-FIG своей "framework interoperability" миссии, так как компоненты HttpFoundation и HttpKernel нельзя малой кровью адаптировать под PSR-7. Апогеем стал pull-request Фабьена "Remove Symfony" в PHP-FIG. Тогда же у Symfony появился никому не нужный альтернативный набор контрактов.
Please open Telegram to view this post
VIEW IN TELEGRAM
Пых
🤨 Задачка со звёздочкой Теперь, когда первая сотня абитуриентов прислала анкеты, предлагаю всем подумать над внеконкурсным заданием. Решите 5-ю задачу с теми же вводными в обратную сторону. То есть у вас должен получиться класс, который, наоборот, позволит…
🎉 Под занавес дня задача поддалась!

Сначала Вадим верно подметил, что суть решения сводится к приостановке потока управления внутри Middleware и предложил использовать для этого генераторы. Затем Александр скинул прототип на базе файберов.

Ну а вот полное решение с использованием Fiber и WeakMap, которое я зафиксировал вскоре после публикации задачи: код, 3v4l. Обсудим подробнее на разборе среза знаний!

P.S.: Всем спокойной ночи!
Please open Telegram to view this post
VIEW IN TELEGRAM
Benchmarking Laravel with Swoole, FrankenPHP, RoadRunner, php-fpm, and ngx-php

https://youtu.be/ZB129Tjkas8
https://github.com/pronskiy/ngx-php_laravel

Позавчера Рома Пронский выложил новый ролик, в котором он забенчмаркал запуск Laravel через основные популярные рантаймы (php-fpm, RoadRunner, Swoole, FrankenPHP) + малоизвестный проект ngx-php, который по результатам Web Framework Benchmarks оставляет позади даже Swoole и workerman.
Пых
👨‍🏫 Срез знаний для 4-го потока Хардкорного курса PHP! https://forms.gle/PFvHT7vujHEPuSs29 Удачи! 😉️️️️️️
😂 Срез знаний Кирилла Несмеянова!

Назови несколько принятых в PHP 8.4 изменений.
Изменено значение у констант PHP_VERSION и PHP_MINOR_VERSION

Почему некоторые расширения, например, ext-pcntl, в composer.json принято прописывать с констрейнтом "*"?
Звёздочка обычно означает примечание, сноску на полях. Возможно, для таких расширений автор прописывает потом примечания автора. Ну, типа, "добавлю-ка я pcntl, сорян виндузятники, но вы идёте лесом".

По какому принципу выбраны значения констант ReflectionProperty::IS_*?
Ребята решили ради прикола написать 1 << 1, 1 << 2, 1 << 3, и т.д., чтобы никто не понял что это значит.

Можно ли расширить возвращаемый тип метода в дочернем классе и почему?
Конечно можно! Ни одной статьи в Конституции РФ или Кодексах за нарушение подобного не предусмотрено. Да и это не такое порицаемое обществом занятие, чтобы ещё за него кто-то осуждал. Всё в рамках приличия.

Как соотносятся понятия "полиморфизм" и "наследование"?
Они оба на написаны кириллицей и в кавычках (только "наследование" хуже, т.к. в нём 12 букв, а "полиморфизм" в этом плане меньше весит, т.к. всего 11 букв).

Интересный факт: Если написать эти слова наоборот, то получится "мзифромилоп" и "еинаводелсан", но их размер не изменится. А ещё из букв в слове "полиморфизм" можно составить слово "зоофил", а из слова "наследование" можно составить "лениновед".

Теперь ты тоже будешь знать это!

Реши задачу.

final readonly class SymfonyIntegrator
{
public function integrate(): void
{
$previous = \getcwd();

try {
\chdir(__DIR__);

\copy('https://getcomposer.org/installer', __DIR__ . '/composer-setup.php');

require __DIR__ . '/composer-setup.php';

new \Symfony\Component\Process\Process([
\PHP_BINARY,
__DIR__ . '/composer.phar',
'create-project',
'symfony/skeleton',
])->run();

// Больше нам PSR фреймворк не нужен
} finally {
$previous && \chdir($previous);
}
}
}


Почему попросить ИИ-помощника написать тесты может быть не очень хорошей идеей?
Нельзя показывать работодателю, что всю твою работу может заменить скрипт на питоне.

В чём оптимизм оптимистичной блокировки?
Разработчики надеются, что в конце-концов когда-нибудь блокировка разблокируется, всё наладится и все будут жить долго и счастливо.

Что ждёшь от курса?
Ну хотя бы доллар по 25, как в началах 2000х. Был бы норм курс.
Please open Telegram to view this post
VIEW IN TELEGRAM
👨‍🏫 Набор на 4-й поток Хардкорного курса завершён!

Все студенты получили письма с приглашениями на почту.

Немного статистики и наблюдений:
108 ответов на момент написания этого поста.
▸ 20 человек набралось на 64-й анкете (в прошлый раз на 41-й), она была отправлена через 54 минуты после старта.
▸ Как будто бы в этот раз вы чаще прибегали к помощи чат-ботов. Некоторые ответы были слишком вышколенными и высокопарными. Программист, который на скорость проходит опрос, так никогда не напишет. В решении 5-й задачи некоторые даже оставили типовые комментарии. В общем, когда я видел явные признаки использования ИИ, я переходил к следующему ответу. И наоборот, к тем, кто по-человечески ошибался в тексте или коде, я был более внимателен.

Сегодня (9 августа) в 19:00 разберём срез и задачу со звёздочкой, приглашаю всех на стрим!

https://youtu.be/wGegvTFidaA

P.S.: Форму не буду закрывать в демонстрационных целях. Заполняйте сколько хотите или используйте на собесах.
Please open Telegram to view this post
VIEW IN TELEGRAM
☠️ eval

Думаю, никому не нужно представлять языковую конструкцию eval. В 2024 все пыхари знают её как древнее могущественное зло, которое ни при каких условиях не должно оказаться на проде. Если в проекте есть хоть один eval, он автоматически считается 💩.

Такое отношение к eval непоследовательно. Если вы используете Symfony Dependency Injection, Doctrine ORM, Ocramius Proxy Manager или любой другой пакет с кодогенерацией, вы фактически используете eval, только завуалированный. С точки зрения безопасности нет никакой разницы между тем, чтобы выполнить код из строки, и тем, чтобы сначала записать строку в файл, а затем его выполнить.

Что на самом деле важно

1. При использовании eval и кодогенерации нужно 1000 раз убедиться, что входные данные либо вообще не могут попасть в код, либо строго санитизируются и правильно интерполируются с использованием var_export() или Typhoon Exporter.

2. Код, исполняемый через eval, require и include получает доступ к текущей области видимости и переменным. Для изоляции можно обернуть вызов в статическое замыкание, см. пример.

На десерт. В коде Symfony 7 есть eval. И не один... А как минимум 3... Уверен, что почти в каждом фреймворке есть. Поищите и скиньте в комментарии.
Please open Telegram to view this post
VIEW IN TELEGRAM
2025/07/01 23:59:45
Back to Top
HTML Embed Code: