Пых – Telegram
Пых
8.28K subscribers
260 photos
14 videos
6 files
566 links
Блог Валентина Удальцова о разработке на PHP.

Хобот @phpyhobot
https://youtube.com/@phpyh
https://vkvideo.ru/@phpyh
https://news.1rj.ru/str/isPHPdying

Статистика: https://news.1rj.ru/str/INOTAROBOT?start=st1219340804

Для связи используйте личные сообщения канала.
Download Telegram
🧑‍🏫 Хардкорный курс по PHP, 2-ой поток (2 группы)

Ну что ж, момент настал! Подготовил базовое описание. Там ещё явно не всё есть. Пишите в комментариях, какие вопросы вас интересуют в первую очередь. Ниже продублирую только информацию о том, как попасть на курс.

Основные требования: наличие компьютера с интернетом и свободное владение русским языком. Никаких ограничений по возрасту, полу и стране происхождения/нахождения нет.

Далее необходимо пройти опрос а-ля "срез знаний" (вспоминаем школу 😅️️), ссылку на него я размещу здесь, на канале Пых, 23 сентября (завтра) в 12:00 по Москве. По результатам среза я сформирую 2 группы по 10 студентов со схожим профилем знаний. При прочих равных приоритет будет отдаваться тем, кто раньше пройдёт опрос.

Желаю удачи!
🔥48👍178😁1
В PHP есть дженерики!

Меня тут осенило, что тип static в PHP (доступен с версии 8.0) — это, по сути, дженерик. В каждом классе его можно представить как ковариантный параметр-тип с ограничением self, который прокидывается по цепочке наследования, каждый раз уточняясь.

/**
* @template-covariant TStatic of A
*/
abstract class A
{
/**
* @return TStatic
*/
abstract public function returnStatic(): object;
}

/**
* @template-covariant TStatic of B
* @extends A<TStatic>
*/
abstract class B extends A
{
}

Пруф в песочнице Psalm вместо тысячи слов: https://psalm.dev/r/a5eda433d5
🤔25👍6🔥5🤯4😱2🤡2😁1👌1
Пых
Срез знаний для "Хардкорного курса PHP"! Форма закрыта 25 сентября. Вопросы скоро разберём на стриме. Копия формы для тех, кому просто интересно, что там было: https://forms.gle/zGZ2s7W7VR2nLDBy8.
🥳 Отбор на "Хардкорный курс PHP" завершён

Ну что ж, я наконец-то обработал 200 заявок на курс, и два десятка студентов получили ночью письма с дальнейшими инструкциями. Большое спасибо за такой интерес, мне очень приятно!

Как именно я отбирал, рассказывать не буду. Скажу лишь, что декораторы, правильно описанные словами, принимались. 😊 Ну и как обещал, у всех был одинаковый шанс попасть на курс: я не изучал колонку с email-ами и не добавлял очков тем, кто присылал мне мотивационные письма в личку.

На ближайшем стриме обязательно обсудим вопросы и задачи из формы.
👍35😢7👏52🔥1
⚓️ Podlodka PHP про тесты!

Вряд ли среди нас найдётся пыхарь, который считает, что тесты вообще не нужны. Здесь всё очевидно. Но вот про само тестирование существует куча холиваров. Верна ли пирамида? Integrated tests are a scam? Класическое или мокистское тестирование? Чистый PHPUnit или дополнительные фреймворки? White box плохо? Нужно ли 100%-ное покрытие или мутационное тестирование? И так далее...

Не знаю, какие из этих вопросов будут затронуты в рамках докладов, но раз собирается знающий народ (Дима Елисеев, ребята из Vi.Tech, Skyeng, SpaceWeb), то обсудить это всё точно получится.

Вот некоторые из заявленных в программе сессий:
• «Как теория тестирования помогает при написании тестов» (Виктор Раев),
• «Как продать автотестирование "бизнесу"» (Виталий Шароватов),
• «Контрактное тестирование (тестирование API)» (Альгис Фатеев),
• «Работа с фреймворками для написания тестов: Codeception» (Евгений Жильцов),
• «Нагрузочное тестирование» (Александр Харченко),
• «Генерация автотестов» (Семен Русин).

https://podlodka.io/phpcrew
👍2010👌1
Enum и память

Вчера мне стало интересно, в какой момент инстанциируются кейсы enum. При декларации или лениво? Логично второе, но я решил проверить при помощи скрипта:

enum A
{
case X;
}

var_dump(spl_object_id(new stdClass())); // int(1)
var_dump(spl_object_id(A::X)); // int(1)
var_dump(spl_object_id(new stdClass())); // int(2)


Тут видно, что сразу после декларации енама A::X не инстанциировался, так как новому объекту был выдан идентификатор 1. А вот при обращении A::X кейс уже завис в памяти, и следующий объект получил идентификатор 2. Короче, логичное вроде бы верно.

Но что-то мне подсказало перепроверить результат. Для этого я сгенерировал "мегаенам" на 60к кейсов, а затем все их запросил, измерив память до и после (скрипт):

$code = 'enum Mega {';
for ($i = 0; $i < 60_000; ++$i) {
$code .= "case C{$i};";
}
$code .= '}';
eval($code);
unset($code);

var_dump(memory_get_usage() / 1024 / 1024); // 14.9

for ($i = 0; $i < 60_000; ++$i) {
constant("Mega::C{$i}");
}

var_dump(memory_get_usage() / 1024 / 1024); // 13.1 😳

И тут я сильно удивился. Потребление памяти не то что выросло, оно упало!!! Не долго думая, я написал Ilija Tovilo, автору PHP RFC: Enumerations. Вот его ответ:

Enums are generally instantiated when accessed, as you expected. The reason why the memory actually decreases was not obvious to me so I had to take a look. Before enums are instantiated, they have a pseudo-representation. That is, an AST node that contains the name of the enum case and its value if the enum is backed. Once that enum case is instantiated the name and value are copied from the pseudo AST node to the actual object, and the AST node is freed. And it turns out that the AST node actually requires more space than the object itself. If you use opcache this won't be the case, because the AST node is stored in shared memory (assuming you don't use eval), whereas the object lives in "userspace".

Выходит, что кейс enum действительно инстанциируется при обращении. Вот только в этот момент ещё и высвобождается псевдо-AST представление этого кейса, а так как оно тяжелее объекта, потребление памяти сокращается.
🤯64🔥38👍244
Valentin Gyver или не софтом единым

Предыстория. Уже много лет я использую трекбол в качестве основного указательного устройства. Во-первых, это удобно: не надо елозить по столу, не нужен специальный коврик. Во-вторых, при игре на барабанах нагрузка на кисти и так достаточно большая, поэтому мне нужно обезопасить себя от туннельного синдрома.

Выбор трекболов на рынке невелик, самый крутой — Logitech MX Ergo. Посудите сами: удобный шарик под большой палец, Bluethooth с поддержкой 2-ух устройств, эргономичный дизайн с изменяемым углом наклона, миддл-клик, отклонение колёсика и ещё 3 кнопки, программируемые в удобном macOS-совместимое приложение Logi Options+. Короче, шедевр.

Конкретно трекболом, который в разных агрегатных состояниях вы видите на фото, я пользуюсь с 2019-ого года (в Happy Inc. у меня был ещё один такой же в офисе). За это время он, конечно, постарел: microUSB разъём для зарядки, аккумулятор держит меньше месяца, левая кнопка мыши иногда пропускает клики. Ну а раз Logitech не обновляет трекбол, трекбол обновляю я!

Накануне забрал запчасти с Алика и вчера ночью решил первые две проблемы: проапгрейдил разъём до Type C и поставил новый аккумулятор большей ёмкости (1200 махов вместо 500). При пайке разъёма, конечно, пришлось поколхозить, потому что Type C шире и китайский вариант, уже готовый для моей простой пятивольтовой задачи, имеет две гигантские ножки. Но в итоге вышло приемлемо. И да, за счёт более ёмкого аккумулятора трекбол стал тяжелее (подковал блоху 😅), но это абсолютно неважно, ведь его не надо двигать! Также сфоткал кнопки левого и правого клика: их закажу и поменяю потом. Кстати, если кто-то в курсе, какие служат дольше, буду очень благодарен.

О результатах судите по фото. В процессе ремонта я испытал куда больший кайф, чем от покупки нового устройства. Этот трекбол точно заслужил такого отношения!
🔥58👍20👏6👎32
Как разбить итератор на батчи?

Представим, что нам надо распарсить какой-то гигантский документ и сформировать вставки в базу по 10к элементов. Для решения этой задачи не обязательно писать кастомный итератор, можно всё сделать "на коленке". Оборачиваем генератор с распарсенными данными в NoRewindIterator, чтобы избежать его перемотки, а затем применяем LimitIterator до тех пор, пока не закончатся данные:

final readonly class Importer
{
private const BATCH_SIZE = 10_000;

public function __construct(private Connection $connection) {}

public function import(): void
{
foreach ($this->parseBatched() as $batch) {
$sqlValues = '';
$values = [];

foreach ($batch as $value) {
$sqlValues .= ($sqlValues === '' ? '' : ',') . '(?)';
$values[] = $value;
}

$this->connection->execute(
'insert into data (val) values ' . $sqlValues,
$values,
);
}
}

private function parseBatched(): Generator
{
$parsed = new NoRewindIterator($this->parse());

while ($parsed->valid()) {
yield new LimitIterator($parsed, limit: self::BATCH_SIZE);
}
}

private function parse(): Generator
{
// ...
}
}
👍74🔥35🤔76🥴31🌭1🗿1
Пых
Valentin Gyver или не софтом единым Предыстория. Уже много лет я использую трекбол в качестве основного указательного устройства. Во-первых, это удобно: не надо елозить по столу, не нужен специальный коврик. Во-вторых, при игре на барабанах нагрузка на кисти…
Ремонт Logitech MX ERGO, часть 2

Вчера я решил третью проблему своего трекбола: поменял кнопки. Оказалось, что оригинальные японские Omron D2FС-F-7N(10M) весьма неплохие, но рассчитаны на 10 миллионов кликов (указано в маркировке), так что, вероятно, они своё честно отслужили. У Omron целая серия таких элементов с разными характеристиками, все они взаимозаменяемы. Но, посоветовавшись с одним мышиным мастером, я в итоге выбрал геймерские Каilh GM 8.0 (80М). Заказал на Авито, так как терпеть ещё месяц прерывающееся перетаскивание невмоготу, а разница в цене для разовой покупки не так принципиальна: 500 рублей за пару против ~300 на AliExpress.

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

В третий раз собрал свой бедный трекбол, и наконец-то всё работает идеально! Надеюсь, несколько лет ещё прослужит.
👍35🔥114
ContainerBuilder в PHP-конфигах Symfony

@oneNevan на курсе показал крутую штуку: в PHP-конфигах Symfony можно запрашивать не только конфигураторы сервисов и расширений DI, но и текущую среду, а также ContainerBuilder!

Последнее безумно удобно в сложных инфраструктурных модулях, которые помимо сервисов добавляют свои CompilerPass-ы, автоконфигурируемые интерфейсы и обработчики атрибутов. Получается, можно не тянуться в Kernel::build через весь проект, чтобы всё это настроить.

В документации Symfony про такие чудеса ни слова. Узнать о них можно лишь "случайно" заглянув в код. 😅
Оказывается, в доке есть пример с ContainerBuilder, спасибо @SymfonyAnton, раскрыл глаза. 👀

Выше я, конечно, подразумевал модульную структуру проекта, при которой конфиг пакета размещается вутри него самого, а не в config (смотрите мой доклад про package-by-feature и модульный скелетон для Symfony).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27🔥121😁1🤡1
Упрощаем тесты с участием файлов

Представим, что мы написали функцию parseCSVFile, которая принимает путь до CSV-файла и возвращает распарсенные данные в удобном нам формате. Как её протестировать?

Первая мысль — написать несколько CSV-файликов, положить их рядом с тестом, а в самом тесте сравнить результат их парсинга с ожидаемыми значениями. Очень просто, но неудобно: тест разбросан по нескольким файлам, сложно читать и вносить изменения.

Вторая мысль — mikey179/vfsstream. Это пакет, который позволяет налету в памяти создавать файловую систему и взаимодействовать с ней как с реальной. Круто, но для тестирования нашей простенькой функции слишком мощно.

А теперь третий вариант, оптимальный. В PHP есть data stream wrapper (а-ля RFC 2397), который позволяет инлайнить содержимое файла прямо в "путь". Формула проста: data://{MIME-тип},{Содержимое}. В итоге тест будет выглядеть так:

$csv = <<<'CSV'
data://text/csv,PHP Version,2022-01,2022-07,2023-01,2023-07
8.0,23.9%,20.6%,16.2%,12.3%
8.1,9.1%,24.5%,38.8%,39.3%
8.2,0.0%,0.0%,4.7%,17.2%
CSV;
$expected = [...];

$parsed = parseCSVFile($csv);

self::assertSame($expected, $parsed);


https://www.php.net/manual/ru/wrappers.data.php
🔥119👍40🤔102🤝1
Ребята обратили внимание, что на моём курсе нет девушек. Это не специально, у меня даже не было данных по полу. Но вот стало интересно, сколько на канале разработчиц?
Anonymous Poll
5%
Я разработчица 👩‍💻
79%
Я разработчик 👨‍💻
16%
Я по приколу 👀
Курсы, деньги, две работы

Вчера с Петром записали подкаст про мои пертурбации в этом году. Рассказал, почему я ушёл с двух работ подряд, как запустил курс хардкорного PHP ну и конечно же про доходы.

https://news.1rj.ru/str/tg_5minphp/1214
👍34🔥16👏21