Если кто-то вдруг пропустил, вчера команда JetBrains запустила информативный и красивый таймлайн развития PHP в честь 25-летия языка.
Советую просмотреть сверху вниз и обратно, обязательно найдёте что-то любопытное. Например, я узнал, что в PHP некогда была функция leak(), что в 2014-ом Википедия перешла на HHVM.
Интересно пронаблюдать, как соотносятся во времени различные события. Например, Дмитрий Стогов присоединился к Zend незадолго до запуска Facebook, а Composer появился гораздо позже, чем большая часть фреймворков.
Ну и конечно не забывайте про скидку 50% на PhpStorm, она действует ещё целый день 😉
https://www.jetbrains.com/lp/php-25/
Советую просмотреть сверху вниз и обратно, обязательно найдёте что-то любопытное. Например, я узнал, что в PHP некогда была функция leak(), что в 2014-ом Википедия перешла на HHVM.
Интересно пронаблюдать, как соотносятся во времени различные события. Например, Дмитрий Стогов присоединился к Zend незадолго до запуска Facebook, а Composer появился гораздо позже, чем большая часть фреймворков.
Ну и конечно не забывайте про скидку 50% на PhpStorm, она действует ещё целый день 😉
https://www.jetbrains.com/lp/php-25/
JetBrains: Developer Tools for Professionals and Teams
25 Years of PHP History
Celebrate the PHP anniversary with JetBrains! Follow the timeline of the pivotal moments in PHP history.
Задачка от меня на канале PHP задачи с собеседований с пояснением к решению.
https://www.tg-me.com/phpquiz/222
https://www.tg-me.com/phpquiz/222
Telegram
PHP задачи с собеседований
Какая функция выведет "php"?
Однажды я услышал, что геттеры — это плохо.
И прошел все этапы реакции по Кюблер-Росс: отрицание, злость, торг, депрессию, принятие 😂
Надеюсь, этот пост поможет пропустить несколько стадий.
DTO. Если тело геттера
Кстати, я тут заметил, что геттер — это трюк. К существительному добавили глагол, чтобы удовлетворить фомуле subject.actionVerb(?object). Вот только get — это не предоставить, а получить. То есть мы не просим объект поделиться состоянием, мы его отбираем.
Value Object. Если метод представляет собой query по CQS и возвращает некоторое представление (проекцию) объекта, то его название не должно быть шаблонным, оно должно отражать семантику. Например,
Entity. Стараемся применять принцип Tell-Don't-Ask. Попробую сформулировать по-своему. Объект — это состояние + поведение. Если мы программируем объектно-ориентированно, мы должны просить объект совершить действие над принадлежащим ему состоянием, а не отбирать у него состояние и выполнять действие за пределами этого объекта. Если программируем не объектно-ориентированно (что тоже ок), то объекты по определению не используем и геттеры, соответственно, тоже.
---
Итак, в DTO вместо геттеров используем публичные свойства. В Value Object — семантические query методы, и то, если не добавлять "на всякий случай", их будет немного. В Aggregate Root оставляем только command методы.
Как тогда вывести состояние модели? Рассудим логически. Для вывода состояния поведение не нужно, нужно только состояние. Значит и сама модель для этой задачи не нужна. В простом случае состояние можно запросить напрямую из хранилища и выдать сразу без всяких ORM:
И прошел все этапы реакции по Кюблер-Росс: отрицание, злость, торг, депрессию, принятие 😂
Надеюсь, этот пост поможет пропустить несколько стадий.
DTO. Если тело геттера
return $this->privateProperty
, заменяем его публичным свойством с аннотацией @psalm-readonly-allow-private-mutation или @psalm-readonly или объявляем весь класс @psalm-immutable. Так мы обеспечиваем инкапсуляцию да ещё и нанооптимизируем код (-N вызовов геттеров). Метод без каких-либо манипуляций не имеет смысла — это 4 строки визуального долга и 1 строка для покрытия тестами. Кстати, я тут заметил, что геттер — это трюк. К существительному добавили глагол, чтобы удовлетворить фомуле subject.actionVerb(?object). Вот только get — это не предоставить, а получить. То есть мы не просим объект поделиться состоянием, мы его отбираем.
Value Object. Если метод представляет собой query по CQS и возвращает некоторое представление (проекцию) объекта, то его название не должно быть шаблонным, оно должно отражать семантику. Например,
Birthday::format(string $format): string
, Color::toHex(): string
.Entity. Стараемся применять принцип Tell-Don't-Ask. Попробую сформулировать по-своему. Объект — это состояние + поведение. Если мы программируем объектно-ориентированно, мы должны просить объект совершить действие над принадлежащим ему состоянием, а не отбирать у него состояние и выполнять действие за пределами этого объекта. Если программируем не объектно-ориентированно (что тоже ок), то объекты по определению не используем и геттеры, соответственно, тоже.
---
Итак, в DTO вместо геттеров используем публичные свойства. В Value Object — семантические query методы, и то, если не добавлять "на всякий случай", их будет немного. В Aggregate Root оставляем только command методы.
Как тогда вывести состояние модели? Рассудим логически. Для вывода состояния поведение не нужно, нужно только состояние. Значит и сама модель для этой задачи не нужна. В простом случае состояние можно запросить напрямую из хранилища и выдать сразу без всяких ORM:
echo json_encode($pdo->query('select x1, x2 from y where z = ?')->fetch())
. В более сложном случае можно посмотреть в сторону Event Sourcing, но про это как-нибудь в другой раз 😉martinfowler.com
bliki: Command Query Separation
a bliki entry for Command Query Separation
Media is too big
VIEW IN TELEGRAM
Открытое собеседование — ищем участников
Бывало, засидишься на одном месте и не знаешь, актуален ли ты еще на рынке... Хотя бы какие там тренды? Что спрашивают-то сейчас вообще на собеседованиях?
Вот и решили с Романом Пронским провести публичное онлайн-собеседование с вопросами на актуальные темы мира PHP.
Трудоустройство не гарантируем, скорее это возможность рассказать о себе, проверить знания, узнать что-то новое да и просто хорошо провести время.
Собеседование будет проходить в режиме стрима в теплой обстановке, примерно как на видео, только я буду без усов 😂
Требования для участия:
• уровень middle/senior;
• PHP 7.x, Composer, PSR;
• ООП, SOLID, coupling/cohesion, вот это все;
• тестирование, PHPUnit;
• желателен опыт с Symfony 4/5;
• SQL, желательно PostgreSQL;
• представление о современных трендах в архитектуре приложений.
Заявки на участие можно отправить до 8 июля через форму: https://forms.gle/ES3nXiwf4ycosGEy9.
Вопросы в личку: @vudaltsov, @pronskiy.
Бывало, засидишься на одном месте и не знаешь, актуален ли ты еще на рынке... Хотя бы какие там тренды? Что спрашивают-то сейчас вообще на собеседованиях?
Вот и решили с Романом Пронским провести публичное онлайн-собеседование с вопросами на актуальные темы мира PHP.
Трудоустройство не гарантируем, скорее это возможность рассказать о себе, проверить знания, узнать что-то новое да и просто хорошо провести время.
Собеседование будет проходить в режиме стрима в теплой обстановке, примерно как на видео, только я буду без усов 😂
Требования для участия:
• уровень middle/senior;
• PHP 7.x, Composer, PSR;
• ООП, SOLID, coupling/cohesion, вот это все;
• тестирование, PHPUnit;
• желателен опыт с Symfony 4/5;
• SQL, желательно PostgreSQL;
• представление о современных трендах в архитектуре приложений.
Заявки на участие можно отправить до 8 июля через форму: https://forms.gle/ES3nXiwf4ycosGEy9.
Вопросы в личку: @vudaltsov, @pronskiy.
Эффективное проектирование агрегатов
Эссе Вона Вернона в трёх коротких частях, которое поможет с пониманием паттерна агрегат.
Прочитав его, вы получите ответы на следующие вопросы:
• как и зачем проектировать маленькие агрегаты;
• что такое истинные инварианты и как их искать;
• почему Value Object предпочтительнее, чем Entity;
• когда нужна мгновенная (immediate), а когда конечная (eventual) согласованность;
• когда позволительно изменять несколько агрегатов в одной транзакции.
По сути, это квинтэссенция нескольких важных глав из синей и красной книги по DDD.
https://dddcommunity.org/library/vernon_2011/
Эссе Вона Вернона в трёх коротких частях, которое поможет с пониманием паттерна агрегат.
Прочитав его, вы получите ответы на следующие вопросы:
• как и зачем проектировать маленькие агрегаты;
• что такое истинные инварианты и как их искать;
• почему Value Object предпочтительнее, чем Entity;
• когда нужна мгновенная (immediate), а когда конечная (eventual) согласованность;
• когда позволительно изменять несколько агрегатов в одной транзакции.
По сути, это квинтэссенция нескольких важных глав из синей и красной книги по DDD.
https://dddcommunity.org/library/vernon_2011/
Иерархические структуры в реляционных СУБД
На этой неделе разгребаю в проекте разные тудушки, поэтому больше читаю и обдумываю, чем пишу код 😂
Вот вам цикл из двух статей, в которых автор сравнивает три подхода к хранению иерархических структур в реляционках:
• список смежных вершин (adjacency list),
• материализованный путь (materialized path),
• вложенное множество (nested set).
Метод вложенных множеств мне всегда казался очень заманчивым, но на работе мы так и не нашли ему применения. Наши наблюдения согласуются с результатами тестов из второй статьи — вложенные множества очень дорогие на вставку/перемещение и при этом не всегда выигрывают на чтение.
В конце первой статьи есть раздел про комбинирование подходов. Особенно удобно комбинировать на проектах с CQRS: списки смежных вершин на запись и списки + материализованные пути на чтение.
https://habr.com/ru/post/46659/
https://habr.com/ru/post/47280/
Забавно. В первой статье есть примеры на Doctrine 1, и они безбожно устарели. Но основной материал будет актуален ещё долго...
На этой неделе разгребаю в проекте разные тудушки, поэтому больше читаю и обдумываю, чем пишу код 😂
Вот вам цикл из двух статей, в которых автор сравнивает три подхода к хранению иерархических структур в реляционках:
• список смежных вершин (adjacency list),
• материализованный путь (materialized path),
• вложенное множество (nested set).
Метод вложенных множеств мне всегда казался очень заманчивым, но на работе мы так и не нашли ему применения. Наши наблюдения согласуются с результатами тестов из второй статьи — вложенные множества очень дорогие на вставку/перемещение и при этом не всегда выигрывают на чтение.
В конце первой статьи есть раздел про комбинирование подходов. Особенно удобно комбинировать на проектах с CQRS: списки смежных вершин на запись и списки + материализованные пути на чтение.
https://habr.com/ru/post/46659/
https://habr.com/ru/post/47280/
Забавно. В первой статье есть примеры на Doctrine 1, и они безбожно устарели. Но основной материал будет актуален ещё долго...
Хабр
Иерархические структуры данных и Doctrine
Введение Хранение иерархических данных (или попросту — деревьев) в реляционных структурах задача довольно нетривиальная и вызывает некоторые проблемы, когда разработчики сталкиваются с подобной...
Как справедливо заметил в обсуждении @dimmount, есть еще четвёртый подход — Closure Table. Мы, кстати, тоже его используем, но я не знал, что это так называется 🙈
По сути, это денормализация материализованного пути в отдельную таблицу ancestor/descendant с заменой like на джойны и подзапросы, подробнее в статье другого автора.
https://habr.com/ru/post/193166/
По сути, это денормализация материализованного пути в отдельную таблицу ancestor/descendant с заменой like на джойны и подзапросы, подробнее в статье другого автора.
https://habr.com/ru/post/193166/
Хабр
Хранение деревьев в базе данных. Часть первая, теоретическая
Полгода назад написал бандл ClosureTable для фреймворка Laravel 3. Поводом для написания стала вот эта замечательная презентация Билла Карвина о способах хранени...
Теперь канал можно поддержать 💵 на Patreon
Для фона долго искал что-то с глубинным смыслом 😂
На всякий случай оставлю тут исходное изображение.
https://www.patreon.com/phpyh
Для фона долго искал что-то с глубинным смыслом 😂
На всякий случай оставлю тут исходное изображение.
https://www.patreon.com/phpyh
Patreon
Get more from Пых on Patreon
создаёт блог о разработке на PHP https://www.tg-me.com/phpyh
В PhpStorm наконец-то добавят поддержку Psalm и PHPStan 🎊
Предположительно, плагины войдут в релиз 2020.3, так что ждём сентябрьский EAP.
Впервые плагины оформлены в виде опенсорс-проектов на GitHub, так что мы можем наблюдать как на языке со статическим анализом реализуется поддержка внешнего статического анализа для языка без статического анализа 🤣 Если серьезно, то проекты можно использовать как эталоны для собственных плагинов.
Подробнее в блоге JetBrains: https://blog.jetbrains.com/phpstorm/2020/07/phpstan-and-psalm-support-coming-to-phpstorm/.
Как отчаянный псалмовец 😜 очень рад быть к этому причастным.
Во время карантина по просьбе @pronskiy записал для команды обзорный скринкаст по фичам Psalm.
Предположительно, плагины войдут в релиз 2020.3, так что ждём сентябрьский EAP.
Впервые плагины оформлены в виде опенсорс-проектов на GitHub, так что мы можем наблюдать как на языке со статическим анализом реализуется поддержка внешнего статического анализа для языка без статического анализа 🤣 Если серьезно, то проекты можно использовать как эталоны для собственных плагинов.
Подробнее в блоге JetBrains: https://blog.jetbrains.com/phpstorm/2020/07/phpstan-and-psalm-support-coming-to-phpstorm/.
Как отчаянный псалмовец 😜 очень рад быть к этому причастным.
Во время карантина по просьбе @pronskiy записал для команды обзорный скринкаст по фичам Psalm.
The JetBrains Blog
PHPStan and Psalm Support Coming to PhpStorm Soon | The PhpStorm Blog
The popular static analyzers PHPStan and Psalm will soon be supported in PhpStorm. Even better, the code behind this support will be open source.It’s always been possible to run PHPStan and Ps
Обработка deadlock в Doctrine
Проблему взаимных блокировок в первую очередь надо решать исходя из контекста, где они возникают. Однако если дедлоки стреляют изредка, можно предусмотреть простой retry. Удобнее всего его реализовать как middleware для command bus и там поймать
Помните, что после ошибки EntityManager закрывается. В Symfony его можно оживить вызовом метода
Пример такого middleware для Symfony Messenger: https://gist.github.com/vudaltsov/945291b4e8a8800f669d478c8d66e8b8
Проблему взаимных блокировок в первую очередь надо решать исходя из контекста, где они возникают. Однако если дедлоки стреляют изредка, можно предусмотреть простой retry. Удобнее всего его реализовать как middleware для command bus и там поймать
Doctrine\DBAL\Exception\RetryableException
.Помните, что после ошибки EntityManager закрывается. В Symfony его можно оживить вызовом метода
resetManager()
на сервисе @doctrine
(автовайрится по интерфейсу Doctrine\Persistence\ManagerRegistry
). После этого менеджер даже в извлеченном из контейнера сервисе не кинет The EntityManager is closed
благодаря замене инициализатора в прокси.Пример такого middleware для Symfony Messenger: https://gist.github.com/vudaltsov/945291b4e8a8800f669d478c8d66e8b8
Gist
DeadlockRetryMiddleware.php
GitHub Gist: instantly share code, notes, and snippets.
Спасибо @andrewDemb за ценное замечание: начиная с PHP 7.4 array_merge может принимать 0 аргументов.
https://3v4l.org/nUEs7
array_merge(...$arrays)
https://3v4l.org/nUEs7
3v4l.org
Online PHP editor | output for nUEs7
Run your php code online; get statistics, vld output and compare output from all versions.
И ещё одно важное уточнение, спасибо @shmaltorhbooks.
Распаковать в аргументы можно только список (индексированный массив). На текущий момент даже Psalm это не проверяет. Соответственно, в общем случае массив массивов нужно привести списку массивов при помощи array_values.
Финальный вариант для PHP >= 7.4:
Для удобства эту конструкцию можно оформить в виде функции в проектном functions.php.
Так приятно, что комьюнити помогает 😊
Распаковать в аргументы можно только список (индексированный массив). На текущий момент даже Psalm это не проверяет. Соответственно, в общем случае массив массивов нужно привести списку массивов при помощи array_values.
Финальный вариант для PHP >= 7.4:
array_merge(...array_values($arrays))
.Для удобства эту конструкцию можно оформить в виде функции в проектном functions.php.
/**
* @template T of array
* @psalm-param array<T> $arrays
* @psalm-return T
*/
function merge_arrays(array $arrays): array
{
return array_merge(...array_values($arrays));
}
Так приятно, что комьюнити помогает 😊
GitHub
Add error when unpacking an array of arguments with string keys · Issue #2481 · vimeo/psalm
Could psalm detect such cases? https://psalm.dev/r/5a1bf51bb9 https://3v4l.org/NNsi8 Note that arrays with integer keys (not lists) can be unpacked https://3v4l.org/ddAYG.
Если вам вдруг когда-нибудь потребуется найти/заменить символы перевода строки (
https://3v4l.org/5cBcW
\r
, \n
, \r\n
), например для нормализации, можно воспользоваться малоизвестным классом \R
.preg_replace('/\R/', PHP_EOL, "text\rwith\nvarious\r\nline endings\n\n")
https://3v4l.org/5cBcW
3v4l.org
Online PHP editor | output for 5cBcW
Run your php code online; get statistics, vld output and compare output from all versions.
В обсуждении @ivan_tsirulev обратил внимание, что
• новую строку
• возврат каретки
• подачу страницы
• вертикальную табуляцию
Пример: https://3v4l.org/6QU82.
Список всех управляющих последовательностей в документации PHP: https://www.php.net/manual/ru/language.types.string.php#language.types.string.syntax.double.
Подробнее о поиске символов новой строки через регулярные выражения в статье Никиты Попова: https://nikic.github.io/2011/12/10/PCRE-and-newlines.html.
\R
матчит более широкий спектр символов:• новую строку
\n
,• возврат каретки
\r
,• подачу страницы
\f
,• вертикальную табуляцию
\v
.Пример: https://3v4l.org/6QU82.
Список всех управляющих последовательностей в документации PHP: https://www.php.net/manual/ru/language.types.string.php#language.types.string.syntax.double.
Подробнее о поиске символов новой строки через регулярные выражения в статье Никиты Попова: https://nikic.github.io/2011/12/10/PCRE-and-newlines.html.
3v4l.org
Online PHP editor | output for 6QU82
Run your php code online; get statistics, vld output and compare output from all versions.
Открытое собеседование #1 🎊
Cтрим в четверг, 16 июля, в 17:00 по Москве/Киеву/Минску
Роман @phpdigest и я @phpyh совместно проведём открытое собеседование с Патриком Фельдешем.
Начнём со знакомства, перейдём к PHP, пробежимся по SOLID и закончим где-то в архитектуре и вопросами из чата. В конце расскажем, что было хорошо, а что не очень, и прошел ли бы кандидат реальное собеседование.
Трансляция будет на новом YouTube канале PHP Point — подписывайтесь, чтобы не пропустить следующие проекты 😉
https://www.youtube.com/watch?v=FQNd9W3nb3A
Cтрим в четверг, 16 июля, в 17:00 по Москве/Киеву/Минску
Роман @phpdigest и я @phpyh совместно проведём открытое собеседование с Патриком Фельдешем.
Начнём со знакомства, перейдём к PHP, пробежимся по SOLID и закончим где-то в архитектуре и вопросами из чата. В конце расскажем, что было хорошо, а что не очень, и прошел ли бы кандидат реальное собеседование.
Трансляция будет на новом YouTube канале PHP Point — подписывайтесь, чтобы не пропустить следующие проекты 😉
https://www.youtube.com/watch?v=FQNd9W3nb3A
YouTube
Открытое собеседование PHP Point #1 / Валентин Удальцов vs Патрик Фельдеш
О Патрике: https://career.habr.com/sspat
Код для ревью: https://gist.github.com/vudaltsov/e6f7dd83a88b349cd5ee0e0d1795e5aa
Задача на SQL: https://gist.github.com/vudaltsov/e3d06ef2158a248337aa262a9fb60b5f
Большое спасибо Антону Мореву за помощь с трансляцией.…
Код для ревью: https://gist.github.com/vudaltsov/e6f7dd83a88b349cd5ee0e0d1795e5aa
Задача на SQL: https://gist.github.com/vudaltsov/e3d06ef2158a248337aa262a9fb60b5f
Большое спасибо Антону Мореву за помощь с трансляцией.…
Мы начинаем открытое собеседование! Присоединяйтесь!
Update: Cтрим закончился, по ссылке запись. Код для ревью и задача по SQL в описании под видео.
https://www.youtube.com/watch?v=FQNd9W3nb3A
Update: Cтрим закончился, по ссылке запись. Код для ревью и задача по SQL в описании под видео.
https://www.youtube.com/watch?v=FQNd9W3nb3A
YouTube
Открытое собеседование PHP Point #1 / Валентин Удальцов vs Патрик Фельдеш
О Патрике: https://career.habr.com/sspat
Код для ревью: https://gist.github.com/vudaltsov/e6f7dd83a88b349cd5ee0e0d1795e5aa
Задача на SQL: https://gist.github.com/vudaltsov/e3d06ef2158a248337aa262a9fb60b5f
Большое спасибо Антону Мореву за помощь с трансляцией.…
Код для ревью: https://gist.github.com/vudaltsov/e6f7dd83a88b349cd5ee0e0d1795e5aa
Задача на SQL: https://gist.github.com/vudaltsov/e3d06ef2158a248337aa262a9fb60b5f
Большое спасибо Антону Мореву за помощь с трансляцией.…
Понравилась статья Null is your friend, not a mistake.
Главный тезис: сама по себе концепция null не является ошибкой (в миллиард долларов), ошибочна система типов Java, которая считает null частью любого типа.
Для понимания. В PHP у нас с этим всё хорошо 😳: мы явно помечаем тип
https://medium.com/@elizarov/null-is-your-friend-not-a-mistake-b63ff1751dd5
За статью спасибо @Enleur.
Главный тезис: сама по себе концепция null не является ошибкой (в миллиард долларов), ошибочна система типов Java, которая считает null частью любого типа.
Для понимания. В PHP у нас с этим всё хорошо 😳: мы явно помечаем тип
X
как nullable при помощи символа вопроса ?X
. В Java такого нет, переменная типа X
может иметь значение X
или null
. Поэтому у новичков (которые не выбрали Kotlin) часто проблемы с NullPointerException, а в коде на Java присутствуют костыли @Nullable
и @NotNull
и/или "избыточные" проверки null != $obj
.https://medium.com/@elizarov/null-is-your-friend-not-a-mistake-b63ff1751dd5
За статью спасибо @Enleur.
Medium
Null is your friend, not a mistake
NullPointerException is dreaded in Java. All sorts of workarounds exist to avoid nulls. Fear not. They are not needed with Kotlin.
Эффективно игнорим исключения в Psalm
Вчера счастью моему не было предела, когда в https://github.com/vimeo/psalm/issues/3286 через 3 месяца мне ответили, что в
Игнорить следует unchecked исключения (подклассы
Кстати, в PhpStorm в разделе Preferences > Languages & Frameworks > PHP тоже можно указывать, какие исключения считать unchecked.
Вчера счастью моему не было предела, когда в https://github.com/vimeo/psalm/issues/3286 через 3 месяца мне ответили, что в
psalm.xml
в разделе <ignoreExceptions>
есть незадокументированный тег <classAndDescendants>
. При помощи него уже давным давно можно игнорить исключение и все его подклассы. На скрине до и после 🤣Игнорить следует unchecked исключения (подклассы
RuntimeException
, LogicException
и Error
). Ну и иногда можно позволить себе игнорить некоторые checked, чтобы не захламлять проект бесконечными @throws
, которые едва ли можно как-то обработать.Кстати, в PhpStorm в разделе Preferences > Languages & Frameworks > PHP тоже можно указывать, какие исключения считать unchecked.