Всем привет!
Мы ежемесячно смотрим новости PHP и Laravel. Самое время поговорить о самом лучшем, самом совершенном, великолепном и бесподобном PHP-фреймворке современности — о Symfony!
Это Кирилл Несмеянов и я постараюсь ежемесячно радовать вас обзором новостей по Symfony. Общаемся если что в тематическом чате в telegram.
Что ждет Symfony в ближайшее время
Ближайшие два месяца сделают нам подарок — в конце ноября выходит версия 7.2. Самое время обсудить новости за прошедший октябрь и рассказать о новом функционале и компонентах.
Несколько дней назад вышла версия 7.2 beta 1 (на момент написания статьи уже RC1), так что весь набор компонентов и функционала уже доступен для тестирования прямо сейчас.
Валидатор
Компонент валидатора предоставил нам 3 новых правила валидации (или 3 новых Assert’а, или Constraint’а, если использовать англицизмы).
Правило «Week» (неделя)
Проверяет переданную неделю на корректность у строки или Stringable-объекта согласно спецификации ISO 8601. В частности, этот формат применяется в соответствующем HTML-элементе <input type="week">
.
Напомню, что стандартный календарный год может содержать 52 или 53 недели. Правило обеспечивает не только корректность формата, но и корректность проверки этого интервала для текущего календарного года согласно указанной локали и времени.
Конечно, вы можете указывать ограничения на допустимый интервал отдельно, без ограничений на минимально и максимально допустимые значения.
use Symfony\Component\Validator\Constraints as Assert; final readonly class BanAccountRequest { public function __construct( #[Assert\Week(min: '2024-W01', max: '2024-W20')] public string|\Stringable $week, ) {} }
Правило «WordCount» (количество слов)
Как и любая проверка, требующая строку, может обрабатывать любые строки (внезапно!) и Stringable-объекты.
Правило проверяет эту «строку» на количество слов. Однако подсчёт количества слов не совсем тривиальная задача и тесно связана с локалью.
Нельзя просто так взять и подсчитать количество пробельных или пунктуационных символов. Для разных языков могут использоваться различные символы для разделения слов. И там, где в одном языке какой-то символ является разделителем, в другом он всего лишь диалектическая особенность. Вспомним, к примеру, перенос одного слова с помощью дефиса в русском языке.
В любом случае, правило полагается на уже готовое и широко применимое расширение intl
, а конкретно класс IntlBreakIterator.
Правило допускает указание максимального и минимального количества слов, а также явное указание локали для конкретного правила.
use Symfony\Component\Validator\Constraints as Assert; final readonly class BanAccountRequest { public function __construct( #[Assert\WordCount(min: 100, max: 200)] public string|\Stringable $lastAccountWish, ) {} }
Правило YAML
Проверяет строку или Stringable-объект на соответствие YAML-формату. Кажется, что сложно придумать применение такому правилу валидации, однако бывают случаи, когда требуется сохранить, например, конфигурацию.
Конечно же, с помощью флагов мы можем указать дополнительные настройки для правил разбора YAML-формата.
use Symfony\Component\Validator\Constraints as Assert; final readonly class UpdateConfigRequest { public function __construct( #[Assert\Yaml] public string|\Stringable $config, ) {} }
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-week-wordcount-and-yaml-constraints
Валидатор (часть 2)
Теперь можно объединять правила валидации в одно большое без написания агрегатов для обработчика. Достаточно отнаследоваться от класса Symfony\Component\Validator\Constraints\Compound
и вернуть массив вложенных проверок.
#[\Attribute] class BetterPasswordValidation extends Compound { protected function getConstraints(array $options): array { return [ new Assert\NotBlank(allowNull: false), new Assert\Length(min: 8, max: 255), new Assert\NotCompromisedPassword(), new Assert\Type('string'), new Assert\Regex('/[A-Z]+/'), // ... ]; } }
Правда, возвращать требуется именно массив, а не iterable
. Это хорошая возможность для контрибьюта в Symfony, так как изменение типа на iterable
не нарушит обратную совместимость.
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-compound-constraint-improvements
Улучшения консоли
Добавлен новый флаг "silent"
, работающий аналогично существующему "--quiet"
, однако исключающий из потока вывода не только логи и отладочную информацию, но и исключения и ошибки.
# Отключает весь "лишний вывод", исключая ошибки php bin/console command-name --quiet # Отключает весь "лишний вывод" php bin/console command-name --silent
Это упростит систематизацию и агрегирование вывода, например в формате JSON, чтобы делегировать их сторонним сервисам и туда не попадало неструктурированных данных. Любители ELK-стека, возрадуйтесь!
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-silent-verbosity
Expression Language
Язык выражений используется для внедрения безопасных конфигурационных выражений, позволяющих императивно описывать правила внедрения зависимостей. Также императивно уточнять правила роутинга (например, для проверки наличия заголовка в запросе для соответствия какому-либо маршруту). Или, например, правил исключения полей DTO при сериализации их в JSON в том же JMS и прочего. Применяется довольно редко на практике, однако почему бы и нет.
use Symfony\Component\ExpressionLanguage\ExpressionLanguage; $el = new ExpressionLanguage(); $el->evaluate('1 << 4'); // result: 16 $el->evaluate('32 >> 4'); // result: 2 $el->evaluate('~5'); // result: -6 $el->evaluate('$a xor $b'); // true если $a или $b true, но не оба
Возвращаясь к новинкам, помимо существующих бинарных операций &
(and), |
(or) и ^
(xor) были добавлены операторы бинарных сдвигов, инвертирование битов (aka бинарный «NOT») и логический XOR.
В версии 7.2 также была добавлена поддержка комментариев в выражениях:
$expression = <<<'EXPRESSION' /* This expression checks that the purchase is made by a customer who doesn't belong to the regular users group */ customer.group == 'vip_customers' or customer.id == 123 /* 123 is an internal test customer */ EXPRESSION;
Вишенкой на торте является модификация сигнатуры конструктора для использования провайдеров iterable
, а не только array
:
app.some_context.expr_lang: class: Symfony\Component\ExpressionLanguage\ExpressionLanguage arguments: $providers: !tagged_iterator app.some_context.expr_lang_provider
Symfony грешит тем, что у многих сервисных компонентов требуются массивы, а не iterable
, из-за чего невозможно передать tagged-итераторы или другие итераторы. Например, Application
symfony консоли, куда в метод addCommands
надо передать массив команд, а не итератор. Из-за этого приходится придумывать обходные пути.
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-expression-language-improvements
Messenger
Раньше для маршрутизации сообщения требовалось указать конкретный транспорт или несколько транспортов, куда данное сообщение будет направлено:
framework: messenger: transports: rabbitmq: "%env(MESSENGER_TRANSPORT_DSN)%" routing: 'App\Message\SomeNotification': rabbitmq
Следуя современным трендам к упрощению сборки через атрибуты, в 7.2 был добавлен атрибут AsMessage
:
#[AsMessage('rabbitmq')] final readonly class SomeNotification {}
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-asmessage-attribute
Messenger (часть 2)
Для решения проблем с таймаутами добавлены:
1. Интерфейс KeepaliveReceiverInterface
с методом keepalive
2. Событие ConsoleAlarmEvent
для сигнала SIGALRM
3. Поддержка флага --keepalive
для команд consume
и retry
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-keepalive-messenger-transports
Symfony Serializer
В версии 7.2 появилась возможность создавать отдельные сериализаторы с полностью отдельными нормализаторами и настройками через секцию "named_serializers"
:
serializer: named_serializers: http_api_v1: name_converter: 'serializer.name_converter.camel_case_to_snake_case' default_context: enable_max_depth: true service_client_v42: default_context: enable_max_depth: false include_built_in_normalizers: false include_built_in_encoders: true
Можно использовать настройки по умолчанию, которые потом дополним или переопределим, или собирать эти настройки с нуля, не используя дефолтные. Ранее проще было зарегистрировать собственный сервис в контейнере, теперь произошла унификация.
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-named-serializers
Переводы (Translation)
Symfony уже включает множество линтеров, таких как проверка синтаксиса конфигурации и корректности настроек контейнера. Ранее существовала команда проверки файлов переводов в формате XLIFF (OASIS стандарт файлов интернационализации, ISO 21720).
В версии 7.2 добавлен общий линтер для валидации файлов переводов в любом формате:
php bin/console lint:translations # Или указываем конкретные локали php bin/console lint:translations --locale=en --locale=ru
Особенно полезно при использовании синтаксиса ICU формата, где легко забыть запятую, закрывающую скобку или ключ "other"
при использовании множественного числа:
--------- -------------------------------- -------- Locale Domains Valid? --------- -------------------------------- -------- ru validators, security, messages Yes en validators, security, messages Yes de messages, validators No --------- -------------------------------- --------
Symfony поддерживает команду "translation:extract"
для автоматического извлечения переводов. В версии 7.2:
1. Добавлена опция "no-fill"
— вместо заглушки вставляет пустую строку
2. В XLIFF-файлах пустая строка заменяет значение с двойным подчеркиванием
Помимо этого, опция sort
теперь применяется к файлам каталога переводов.
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-translations-linter
Контейнер
Начиная с версии 5.3 можно указывать окружения для регистрации сервисов. Например, отладочная команда для локальной среды:
#[When(env: 'dev')] #[AsCommand(name: 'test', description: 'Дропаем базу к чертям собачьим...')] final class ExampleDebugCommand extends Command { // ... }
В 7.2 добавили атрибут WhenNot
для регистрации сервиса на всех окружениях кроме указанного:
#[WhenNot(env: 'dev'), WhenNot(env: 'test')] #[AsCommand(name: 'doctrine:fixtures:load', description: 'Защита от дураков')] final class FixtureDisablerCommand extends Command { // ... }
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-whennot-attribute
Формы
Фреймворк предоставляет компонент EntityType
для создания select
-элементов, где значения берутся из сущности. Проблема в том, что при большом количестве значений браузер может зависнуть при отрисовке.
В Symfony 7.2 добавили опцию choice_lazy
, позволяющую отключить принудительную отрисовку всех опций. За загрузку данных отвечает LazyChoiceLoader
, который можно модифицировать.
В качестве альтернативы можно использовать поле автодополнения из набора компонентов Symfony UX, предоставляющее гибридную реактивную интеграцию бэкенда и фронтенда для простых случаев (например, админок).
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-lazy-choice-loader
Улучшения компонента работы со строками
Добавлен метод kebab
— аналог snake_case
, использующий дефисы вместо подчёркивания. Теперь вы можете официально «кебабить» в рабочее время и вам ничего за это не сделают!
use function Symfony\Component\String\u; $string = u('symfonyIsGreat')->kebab(); // $string = 'symfony-is-great' $string = u('Symfony is great')->kebab(); // $string = 'symfony-is-great' $string = u('Symfony_is_Great')->kebab(); // $string = 'symfony-is-great'
Ранее можно было обрезать строку… Но не просто по буквам! Второй аргумент "cut"
позволял оставить в сохранности последнее слово.
u('Lorem Ipsum')->truncate(8); // 'Lorem Ip' u('Lorem Ipsum')->truncate(8, cut: false); // 'Lorem Ipsum'
Начиная с версии 7.2, помимо булева значения также доступны режимы
транкейта TruncateMode
для «округления» результата обрезки в большую или меньшую сторону.
u('Lorem ipsum dolor')->truncate(8, cut: TruncateMode::Char); // 'Lorem ip' u('Lorem ipsum dolor')->truncate(8, cut: TruncateMode::WordBefore); // 'Lorem' u('Lorem ipsum dolor')->truncate(8, cut: TruncateMode::WordAfter); // 'Lorem ipsum'
Ну и последнее. Добавлен испанский инфлектор (склонятор слов).
use Symfony\Component\String\Inflector\SpanishInflector; $inflector = new SpanishInflector(); $inflector->singularize('aviones'); // returns ['avión'] $inflector->pluralize('miércoles'); // returns ['miércoles']
Теперь вы можете множить испанские слова без испанского стыда. Такие дела.
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-string-component-improvements
Нотификатор и Mailer
Добавлены новые драйверы и интеграции:
Для нотификатора:
— Primotexto
— Sipgate
— Sweego
— LINE Bot
Для Mailer:
— Mailchimp
— Sweego
— Mailomat
— Postal
— Mailtrap
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-mailer-and-notifier-integrations
Также для нотификаций
В симфони 7.2 добавлили драйвер нотификаций для… Рабочего стола!
Теперь вы можете слать нотификации прямиком на рабочий стол. Крайне удобно для какого-либо дебага. И крайне неудобно, если вы будете делать это на сервере.
use Symfony\Component\Notifier\Message\DesktopMessage; $notification = new DesktopMessage( 'Something Went Wrong', 'Котаны, у вас там прод упал…', );
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-desktop-notifications
Улучшения ядра (Framework Bundle)
Темплейт-контроллер теперь позволяет добавлять дополнительные заголовки ответа для статики:
welcome: path: /static/some-icon controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController defaults: template: 'static/icons/some.svg.twig' headers: content-type: 'image/svg+xml'
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-template-dx-improvements
Symfony/Twig bridge
Для упрощения частичной отрисовки можно использовать методы-хелперы RenderBlock
и RenderBlockView
из AbstractController
. Это удобно при использовании Symfony UX Turbo или HTMX. В версии 7.2 функционал упростили — достаточно вернуть данные с указанием имени блока и шаблона.
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-template-dx-improvements
Компонент конфигурации
Добавлен метод parameterCannotBeEmpty
:
$container->parameterCannotBeEmpty( 'app.private_key', 'Value for "app.private_key" must be defined', );
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-non-empty-container-parameters
Symfony MIME
Во-первых, ранее список энкодеров для объекта TextPart
был жёстко захардкожен и представлял из себя 3 типа: “quoted-printable”
, “base64”
и “8bit”
. Теперь можно добавлять пользовательские энкодеры, что потребует реализовать 3 метода.
class MyEncoder implements ContentEncoderInterface { public function encodeByteStream(...): iterable { … } public function encodeString(...): string { … } public function getName(): string { … } }
Зарегистрировать и использовать энкодер довольно просто:
TextPart::addEncoder(new MyEncoder()); $content = new TextPart('...', 'utf-8', 'plain', 'my_encoder');
Конечно, пихать всё возможное в статику — довольно странно, однако… Почему бы и нет?
Во-вторых, добавлена поддержка юникода для почты согласно RFC 6531. Не латинские символы теперь возможно использовать не только для имени почты, но и для домена.
В новости об этом функционале ничего не сказано об автоматическом преобразовании таких имён в Punycode, который используется для обратной совместимости для юникодных имён. Так что имя в юникоде используется «как есть».
Подробнее: https://symfony.com/blog/new-in-symfony-7-2-mime-improvements
На этом новости о Symfony 7.2 за октябрь закончены
Дайджест в формате видео:
ссылка на оригинал статьи https://habr.com/ru/articles/859224/
Добавить комментарий