Привет, Хабр!
Сергей Пантелеев, Кирилл Несмеянов и Данил Щуцкий собрали новости за ноябрь в PHP, Symfony и Laravel (соответственно). Всё самое интересное. Если вы хотите быть в теме происходящего, этот материал точно для вас. ?
Новости PHP
Вышел PHP 8.4.1
PHP 8.4 – большое обновление языка. Оно содержит множество новых возможностей, таких как хуки свойств, асимметричная область видимости свойств, обновление DOM API, ленивые объекты и многое другое. Как обычно, улучшена производительность и исправлены ошибки.
Исправления безопасности, о которых мы поговорим ниже, не попали в сборку PHP 8.4.0, поэтому цикл PHP 8.4 начался с версии PHP 8.4.1.
Не полагайтесь на тег php-8.4.0, если собираете PHP из исходных кодов.
В день выхода PHP 8.4, на канале CutCode, мы с видными представителями PHP-сообщества – Кириллом Несмеяновым, Алексеем Гагариным, Александром Макаровым, Валентином Удальцовым, Павлом Бучневым и Дмитрием Елисеевым – обсудили главные фишки релиза и поговорили о PHP в целом. Обязательно посмотрите, если пропустили.
Вышли PHP 8.1.31, PHP 8.2.26 и PHP 8.3.14
В этих выпусках исправлены сразу 6 ошибок безопасности:
Heap-Use-After-Free при обработке sapi_read_post_data в интерфейсе CLI SAPI (GHSA-4w77-75f9-2c8w).
Доступ к внеполосным данным в функции ldap_escape (GHSA-g665-fm4p-vhff).
Утечка части содержимого кучи через перечитывание буфера кучи (GHSA-h35g-vwh6-m678).
Целочисленное переполнение в квотере dblib и firebird, приводящее к записи внеполосных данных (GHSA-5hqh-c84r-qjcv).
Настройка прокси в контексте потока может позволить вводить CRLF в URI (GHSA-c5f2-jwm7-mmq2).
Перечитывание одного байта с помощью фильтра convert.quoted-printable-decode (GHSA-r977-prxv-hc43).
Пожалуйста, обновитесь как можно скорее.
PHP.net is temporarily unavailable
Некоторые из вас могли заметить, что 24 ноября на протяжении 12 часов был недоступен сайт php.net. Нет, не потому что накатили PHP 8.4 и все сломалось, проблемы были на стороне CDN-провайдера сайта.
К чему это я, shit happens… Надо лишь разбирать свои ошибки и стараться их не повторять. Команда PHP сейчас работает над Post-Mortem отчетом для минимизации подобных проблем в будущем.
Вышел Composer 2.8.0
В новой версии Composer появилось несколько интересных дополнений:
Флаг
--patch-only
, который позволяет ограничить обновления изменениями на уровне патча, минимизируя риск внесения изменений, нарушающих обратную совместимость.Возможность явно определить, нужно ли передавать дополнительные аргументы/опции в основную команду.
Анонсирован установщик модулей PHP (PIE)
PHP Foundation представил предварительный релиз PHP Installer for Extensions (PIE). PIE призван упростить управление модулями PHP, предоставляя современную, гибкую альтернативу репозиторию PECL.
Модули распространяются через Packagist так же, как и обычные PHP-пакеты. Процесс установки и обновления покажется вам знакомым, если вы уже используете Composer.
Вышел API Platform 4
Новая версия фреймворка для создания REST- и GraphQL API-приложений теперь официально поддерживает Laravel.
Вышел PHPStan 2.0
Первая версия PHPStan была выпущена чуть более трех лет назад. С тех пор команда сделала более 170 релизов, внедряя новые функции, исправляя ошибки и закладывая основу для второй версии.
В новой версии PHPStan добавлен более строгий 10 уровень анализа кода, добавлена поддержка типа list
, уменьшено потребление памяти, улучшена производительность и многое другое.
? PHP Foundation исполнилось 3 года
Фонд PHP Foundation был основан три года назад.
За прошедший год PHP Foundation поддержал работу 10 основных разработчиков и внёс значительный вклад в развитие языка PHP.
Поддержать PHP Foundation можно с помощью OpenCollective или GitHub Sponsors.
Вышел PhpStorm 2024.3
В PhpStorm 2024.3 добавлены инспекции и быстрые исправления, упрощающие переход на PHP 8.4.
Помимо поддержки PHP 8.4, главные новинки этой версии:
Улучшенный AI Assistant
Поддержка xdebug_notify()
Интерпретатор PHP из Laravel Herd
Вышел Laravel Idea 9
Вышла девятая версия плагина Laravel Idea для PhpStorm.
В новой версии добавлено создание модели Eloquent из существующей таблицы базы данных, возможность легко запускать Artisan команды, добавлена поддержка фасадов в реальном времени и много другое!
Разработчики из России, Белоруссии и Украины могут подать заявку на получение бесплатного ключа на сайте laravel.su.
Кстати, Адель Файзрахманов – автор Laravel Idea – принимал участие в пилотном выпуске нашей викторины по PHP, посмотрите, если пропустили.
PHP Russia 2024
В Москве прошла конференция Highload, в рамках которой 16 докладов выделены под PHP Russia. Командой CutCode мы поехали на конференцию и скоро выпустим для вас небольшой видео отчёт.
Пробрались за кулисы, поснимали внутреннюю кухню, поговорили с организаторами, докладчиками и зрителями.
«Своя игра» по PHP #4
За прошедший год мы провели уже три викторины по PHP и под конец года мы организуем викторину среди победителей предыдущих игр. В финальной игре этого года встретятся Кирилл Несмеянов, Алексей Гагарин и Павел Бучнев.
Присылайте свои вопросы ребятам, некоторые мы добавим в игру или обсудим их в перерывах между раундами, а автор самого интересного вопроса, получит слоника.
PHP Core Roundup
Большинство новостей ядра PHP подробно освещаются в серии PHP Core Roundup от PHP Foundation, мы лишь быстро по ним пробежимся:
✅RFC: Add persistent curl share handles
Сейчас модуль cURL не поддерживает постоянные дескрипторы совместного доступа cURL. Соединения передаются только между дескрипторами в рамках одного SAPI-запроса.
В PHP 8.5 появится новая функция curl_share_init_persistent()
, которая позволит сохранять дескрипторы cURL в глобальной памяти и повторно использовать их в последующих запросах. Улучшение направлено на повышение производительности за счет снижения накладных расходов на инициализацию дескрипторов cURL каждый раз во время их использования.
Matthieu Napoli, автор Bref:
Большинство PHP-приложений проводят большую часть своего времени, выполняя операции ввода-вывода (не используя процессор). <...>
Но вызов API (через HTTP) может занимать очень много времени. Разные порядки величины.
Почему? Потому что каждый раз устанавливается TPC и HTTPS-соединение. Это может занять 100 мс или 200 мс (HTTPS – это безумие). На каждый запрос, потому что PHP (с FPM) каждый раз создает соединение заново.
Вот почему переход на Laravel Octane или Symfony Runtime иногда имеет такое значение: поддерживая процесс PHP между запросами, мы можем сэкономить эти 100 мс. По сравнению с тем, что мы экономим на времени загрузки фреймворка, это очень много.
RFC предлагает разделять HTTP-соединения между запросами в PHP-FPM: это очень важно, потому что может принести некоторые из этих преимуществ всем PHP-приложениям, без необходимости переходить на Octane/Runtime (и связанные с ними недостатки).
✅RFC: Support Closures in constant expressions
Некоторые конструкции PHP ограничены возможностью принимать только постоянные выражения. Эти выражения могут содержать только ограниченное количество операций, которые можно кратко охарактеризовать как неизменяемые значения.
Примечательно, что параметры атрибутов – это конструкция, которая принимает только постоянные выражения, а замыкания в настоящее время – не входят в набор разрешенных операций.
Поскольку замыкания – это фактически просто исходный PHP-код (или, скорее, опкоды PHP), они являются неизменяемыми значениями, и поэтому нет никаких фундаментальных причин, по которым они не должны быть разрешены в постоянных выражениях.
Tim Düsterhus и Volker Dusch предлагают разрешить передавать замыкания в параметры атрибутов, значения свойств, параметры по умолчанию, константы, а также константы классов.
final class Locale
{
#[Validator\Custom(static function (string $languageCode): bool {
return \preg_match('/^[a-z][a-z]$/', $languageCode);
})]
public string $languageCode;
}
? RFC: Records
Value Object – это неизменяемый объект, который представляет некоторое значение или концепцию в приложении.
Robert Landers предлагает добавить новое ключевое слово record определения неизменяемых Value Object.
Объекты Record могут реализовывать интерфейсы и использовать трейты, но не могут расширять другие объекты Record или классы.
У каждого объекта Record также будет доступен метод with для частичного обновления свойства, создавая новый экземпляр объекта с обновленными свойствами.
record Planet(string $name, int $population);
$pluto = Planet("Pluto", 0);
$pluto = $pluto->with(population: 1);
? RFC: Data Class
Еще один RFC от Robert Landers, который появился благодаря обсуждению RFC про объекты Records.
Robert предлагает добавить новый модификатор класса: data, который радикально поменяет работу классов, делая их сравнимыми по значению, а не по ссылке, и любые мутации ведут себя скорее как массивы, чем как объекты. При желании его можно комбинировать с другими модификаторами, такими как readonly, для обеспечения неизменяемости.
data class Rectangle {
public function __construct(public int $width, public int $height) {}
public function area(): int {
return $this->width * $this->height;
}
public function resize(int $width, int $height): static {
$this->height = $height;
$this->width = $width;
return $this;
}
}
$rectangle = new Rectangle(10, 20);
$newRectangle = $rectangle;
$newRectangle->width = 30;
$otherRectangle = new Rectangle(30, 20);
assert($rectangle !== $newRectangle); // true
assert($newRectangle === $otherRectangle); // true
$bigRectangle = $rectangle->resize(10, 20);
assert($bigRectangle !== $rectangle); // true
✅ RFC: Policy on 3rd party code
В PHP долгое время был негласный запрет на использование и упоминание сторонних фреймворков и инструментов, чтобы не отдавать предпочтение какой-либо экосистеме, даже если какой-то инструмент является де-факто стандартом отрасли (например, Composer), либо уже используется в PHP (например, DokuWiki).
Larry Garfield предложил принять политику о сторонних проектах, которые может использовать или упоминать PHP.
? RFC: PHP.net Analytics
Еще один RFC, напрямую не связанный с разработкой – добавление трекера аналитики на сайт php.net.
Larry Garfield и Роман Пронский предложили установить трекер Matomo, который будет собирать обезличенную информацию о поведении пользователей на сайте, кроме того, данные трекера будут доступны только команде PHP Infrastructure и не будут передаваться третьим лицам.
На данный момент документация PHP содержит более 17 000 страниц, трекер поможет оптимизировать работу над документацией, а также понять поведение пользователей на сайте.
Драма WordPress и WP Engine
Вокруг WordPress разгорается скандал. Проблемой стал конфликт между Matthew Mullenweg, одним из создателей WordPress и CEO компании Automattic, и WP Engine, хостингом для сайтов, созданных на WordPress.
В середине сентября Matthew Mullenweg опубликовал в своём блоге сообщение, в котором назвал WP Engine «раковой опухолью WordPress». Он раскритиковал хостера за отключение истории изменений для каждой записи. Matthew Mullenweg считает, что эта функция лежит в основе «обещания защитить данные пользователя», а WP Engine, отключая её, просто пытается сэкономить.
Он также обратился к инвестору WP Engine, компании Silver Lake, которую обвинил в недостаточном вкладе в проект. Кроме того, он заявил, что использование бренда WP хостингом WP Engine вводит пользователей в заблуждение: хостинг необоснованно позиционирует себя как часть WordPress.
После этого Matthew Mullenweg запретил WP Engine доступ к ресурсам WordPress. Этот шаг сломал множество сайтов, помешав им обновлять плагины и темы. Часть ресурсов стала уязвимее для кибератак, что вызвало возмущение среди сообщества.
Эта новость не попала бы в дайджест, если бы в социальных сетях неравнодушные пользователи не обрушились с критикой на PHP Foundation за то, что фонд поблагодарил компанию Automattic за спонсорскую помощь. Некоторым показалось неуместным благодарить компанию в подобной ситуации.
Laravel дайджест
Обновления Laravel
[11.30] Allow the authorize method to accept Backed Enums directly
https://github.com/laravel/framework/discussions/53330
PR продолжает развивать тему с Enum, и теперь в контроллере в методе Authorize также допускаются Enum.
enum DashboardPermission: string
{
case VIEW = 'dashboard.view';
}
public function index(): Response
{
$this->authorize(DashboardPermission::VIEW);
//
}
[11.30] use exists() instead of count()
https://github.com/laravel/framework/pull/53328
Небольшой pull request, который с улучшением оптимизации. До этого под капотом использовался count, что не очень хорошо. Его заменили на exists.
[11.30] Introduce HasUniqueStringIds
https://github.com/laravel/framework/pull/53280
Следующий pull request у нас затрагивает Eloquent модели - возможности взаимодействовать с UUID и с ULID. Но при этом автор пишет, что существует проблема - проверка валидности вшита в метод resolveRouteBindingQuery
. И если нам потребуется полностью изменить логику и использовать собственные идентификаторы, то тем самым вместе с этим придется и полностью переопределить огромный метод resolveRouteBinding
для того, чтобы заменить всего одно условие в двух местах:
trait HasTwrnsTrait
{
use HasUuids;
public function newUniqueId(): string
{
return (string) Twrn::new();
}
public function resolveRouteBindingQuery($query, $value, $field = null)
{
if ($field && in_array($field, $this->uniqueIds()) && ! Twrn::isValid($value)) {
throw (new ModelNotFoundException)->setModel(get_class($this), $value);
}
if (! $field && in_array($this->getRouteKeyName(), $this->uniqueIds()) && ! Twrn::isValid($value)) {
throw (new ModelNotFoundException)->setModel(get_class($this), $value);
}
return parent::resolveRouteBindingQuery($query, $value, $field);
}
}
Автор предлагает решение:
trait HasTwrnsTrait
{
use HasUniqueStringIds;
public function newUniqueId()
{
return (string) Twrn::new();
}
protected function isValidUniqueId($value): bool
{
return Twrn::isValid($value);
}
}
Видим, что у нас трейт в трейте, он создал еще один трейт. Дубли, которые присутствовали в максимальном количестве в двух трейтах до этого, были вынесены в новый трейт и соответственно добавлен новый метод, он абстрактный и реализуется в трейтах на уровень выше. Получается что там, где у нас была вшита логика по ULID и UUID, она поменялась на этот метод. Возможно, скажете, что много странностей, но так бывает, вот такая возможность появилась в этом pull request.
Казалось бы, логика ULID и UUID, а при этом дают возможность генерировать в них все что угодно, выходя за ответственность этих трейтов. Все это говорит о том, что в целом обсуждения по линчеванию Laravel, которые мы делали недавно и не требуются. Все странные вещи в Laravel можно посмотреть в обновлениях Laravel, где мы постоянно натыкаемся на странные решения. Это обновление - яркий пример
[11.30] Add withoutDefer and withDefer testing helpers
https://github.com/laravel/framework/pull/53340
Следующему pull request, затрагивает он тесты с отложенным выполнением, и на уровне тестов мы можем воспользоваться методами withDefer
, withoutDefer
, чтобы тестировать с или без выполнения отложенных функций.
[11.31] Cache token repository
https://github.com/laravel/framework/pull/53428
Первый pull request в 11.31 затрагивает сброс паролей. По умолчанию у нас был только дефолтный драйвер, который хранил токены в базе данных. Дополнительно появился еще драйвер для хранения в кэше.
'passwords' => [
//new cache driver
'customers' => [
'driver' => 'cache',
'store' => 'passwords',
'provider' => 'customers',
'expire' => 60,
'throttle' => 60,
],
//default old database driver
'users' => [
'provider' => 'users',
'table' =>'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
Вот мы его видим в примере и можем использовать.
[11.31] Add ability to dynamically build mailers on-demand using Mail::build
https://github.com/laravel/framework/pull/53411
Двигаемся дальше к следующему pull request в 11.31. Steve Bauman представляет свой Builder в рамках отправки мейлов в рамках класса Mail, чтобы прямо на лету сбилдить конфигурацию и сразу ее использовать.
use Illuminate\Support\Facades\Mail;
$mailer = Mail::build([
'transport' => 'smtp',
'host' => '127.0.0.1',
'port' => 587,
'encryption' => 'tls',
'username' => 'usr',
'password' => 'pwd',
'timeout' => 5,
]);
$mailer->send($mailable);
Как видим в примере, все понятно без объяснения.
[11.31] Add ability to dynamically build cache repositories on-demand using Cache::build
https://github.com/laravel/framework/pull/53454
Переходим к следующему pull request, и все тот же Steve Bauman сильно вдохновился своим Builder конфигурации на лету, и то же самое добавил и для кэша.
use Illuminate\Support\Facades\Cache;
$apc = Cache::build([
'driver' => 'apc',
]);
$array = Cache::build([
'driver' => 'array',
'serialize' => false,
]);
$database = Cache::build([
'driver' => 'database',
'table' => 'cache',
'connection' => null,
'lock_connection' => null,
]);
$file = Cache::build([
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
]);
Все понятно.
[11.31] Add DB::build method
https://github.com/laravel/framework/pull/53464
Двигаемся к следующему pull request, и у нас все еще Steve Bauman, который никак не мог угомониться и добавил то же самое еще и в DB.
use Illuminate\Support\Facades\DB;
$sqlite = DB::build([
'driver' => 'sqlite',
'database' => ':memory:',
]);
$mysql = DB::build([
'driver' => 'mysql',
'database' => 'forge',
'username' => 'root',
'password' => 'secret',
]);
$pgsql = DB::build([
'driver' => 'pgsql',
// ...
]);
$sqlsrv = DB::build([
'driver' => 'sqlsrv',
// ...
]);
В примерах все понятно.
[11.31] Added useCascadeTruncate method for PostgresGrammar
https://github.com/laravel/framework/pull/53343
На стриме у Валентина мы обсуждали проблемы Laravel. Кирилл Мокевнин рассказывал о том, что у него был такой кейс с транкейтом, где он в миграциях вызвал транкейт и в рамках Postgres было нестандартное поведение, так как под капотом Laravel на уровне Postgres использует вместо дефолтного рестрикта каскадный подход. Соответственно, автор pull request явно смотрел этот линч и недолго думая пошел и сделал вот такой pull request, добавив метод useCascadeTruncate
в PostgresGrammar и чтобы не делать никаких брейкинг-ченджей, сразу сказал Тейлору, что в 12 и 13 версиях можно будет сделать дефолтное нормальное поведение, но при этом, кому понравились странности из прошлого, смогут использовать его новый метод. Тейлор, видимо, тоже смотрел линч Валентина, плюс прочитал подробное описание и, стиснув зубы, смёржил этот pull request. А мы с вами двигаемся дальше, но заодно делаем себе пометку, что наши стримы смотреть полезно, после них можно пойти сделать pull request и узнать что-то новое под капотом Laravel.
[11.31] Add the ability to append and prepend middleware priority from the application builder
https://github.com/laravel/framework/pull/53326
Продолжается тема с мидлварами в рамках нового, относительно нового со времен 11 Laravel, application builder. Появился новый метод append
и prepend
. Сразу набор определенных мидлваров, либо до, либо после указанного.
$middleware->appendToPriorityList(after: $middlewareToPutTheNewMiddlewareAfter, append: $theMiddlewareToAppend);
$middleware->prependToPriorityList(before: $middlewareToPutTheNewMiddlewareBefore, prepend: $theMiddlewareToPrepend);
Ну и немного изменили сигнатуру, добавились наименования after
, before
, append
и prepend
. В общем, вот такой удобный сахар, который мы с вами будем использовать.
[11.31] Add URL::forceHttps() to enforce HTTPS scheme for URLs
https://github.com/laravel/framework/pull/53381
Следующий pull request в рамках класса URL добавиляет метод forceHttps
, и независимо от того, какая у вас в реальности схема, все генерируемые URL в рамках вашего приложения будут иметь схему HTTPS.
URL::forceHttps(
$this->app->isProduction()
);
Я думаю, многие из вас будут рады этому новому методу.
[11.32] Http Client: fake connection exception
https://github.com/laravel/framework/pull/53485
Перемещаемся к 11.32. Pull request затрагивает HTTP клиент в рамках тестов. До этого мы не могли отслеживать connection exception:
Http::fake(['https://laravel.com' => Http::failedConnection()]);
// Similar syntax to:
Http::fake(['https://laravel.com' => Http::response()]);
теперь мы можем передать failed connection и в итоге сымитировать это поведение.
[11.32] Add support for syncing associations with array or base collection of models
https://github.com/laravel/framework/pull/53495
Интересный pull request в рамках отношений belongs to many. До этого для синхронизации через метод sync нам нужно было передавать ID, теперь же нам доступна сразу передача моделек, всегда удивлялся, почему этого не было изначально, но теперь так делать можно.
$tag = Tag::create(['name' => Str::random()]);
$tag2 = Tag::create(['name' => Str::random()]);
- $post->tags()->sync([$tag->id, $tag2->id]);
+ $post->tags()->sync([$tag, $tag2]);
То же самое еще добавили и с коллекциями. То есть можно и не в виде массива, а сразу передать коллекцию, и это тоже будет работать без каких-то дополнительных методов.
- $post->tags()->sync(Arr::pluck($tags, 'id'));
+ $post->tags()->sync($tags);
[11.32] Introduce Schedule Grouping
https://github.com/laravel/framework/pull/53427
Следующий pull request у нас затрагивает расписание, Schedule
. Была проблема, собственно, как и в роутах, только здесь не настолько распространено, так как команды не в таком количестве и не так часто они имеют одинаковые настройки, но в целом такой кейс существовал, и теперь можно объединять вызовы команд по расписанию в определенные группы, чтобы не дублировать портянку из общих методов.
Schedule::group()
->everyMinute()
->runInBackground()
->withoutOverlapping()
->schedules(function () {
Schedule::command('command-one');
Schedule::command('command-two');
Schedule::command('command-three');
});
Schedule::group()
->everyMinute()
->runInBackground()
->withoutOverlapping()
->schedules(function () {
Schedule::command('command-one');
Schedule::command('command-two');
Schedule::command('command-three')->everyTenMinutes(); // Override the group's cron expression
});
Schedule::group()
->runInBackground()
->withoutOverlapping()
->schedules(function () {
Schedule::group()->everyMinute()->schedules(function () {
Schedule::command('command-one');
Schedule::command('command-two');
});
Schedule::group()->everyTenMinutes()->schedules(function () {
Schedule::command('command-three');
Schedule::command('command-four');
});
});
[11.32] Add "head" slot to email layout
https://github.com/laravel/framework/pull/53531
В Blade-шаблоне для отправки email-уведомлений появился slot-head, чтобы можно было добавить стили, что-либо еще в head-блок наших писем.
<x-slot:head>
<style>
@media only screen and (max-width: 600px) {
.inner-body {
border-radius: 0 !important;
}
}
</style>
</x-slot:head>
[11.33] Add "createQuietly" method
https://github.com/laravel/framework/pull/53558
Pull request в моделе добавляет новый метод createQuietly
. Вроде бы он был раньше, но, как оказалось, нет. Добавили в 11.33, чтобы создать и при этом не дергать никаких эвентов.
[11.33] Add Request::enums method to retrieve an array of enums
https://github.com/laravel/framework/pull/53540
Следующий pull request снова от Steve Bauman, мы его сегодня в дайджесте видели уже многократно, он делал билдеры для всего, что только можно. В данной ситуации он прокачал объекты реквеста.
// app/Http/Controllers/WebhookController.php
public function store(Request $request)
{
$request->validate([
'url' => 'required|url',
'events' => 'required|array',
'events.*' => Rule::enum(WebhookEvent::class),
]);
// [WebhookEvent::UserCreated, WebhookEvent::UserUpdated]
$events = $request->enums('events', WebhookEvent::class);
}
До этого был метод enum
, который строку из реквеста трансформировал в определенный enum. Также появился enums
, когда в реквесте массив, и мы сразу трансформируем в массив enum.
Webhook::create([
'events' => $request->enums('events', WebhookEvent::class),
// ...
]);
[11.33] prefer new Collection() over collect()
https://github.com/laravel/framework/pull/53563
Интересный pull request. Автор устал от того, что в ядре используется под капотом и переделал все упоминания на instance collection напрямую. Если заглянем, у нас много изменений, и соответственно везде исправлено в надежде, что со временем код будет становиться лучше, и вот такие моменты будут исправляться.
[11.33] PHP 8.4 Code compatibility
https://github.com/laravel/framework/pull/53571
Следующий pull request у нас затрагивает совместимость с PHP 8.4. Кстати, мы делали стрим, где рассматривали подробно с крутыми гостями каждую фичу и обсуждали, что вообще в целом у нас 8.4 и что нас ждет в дальнейшем, поэтому кто не смотрел, обязательно переходите и посмотрите. И как видим по изменениям, их не так много, чтобы добавить эту совместимость.
[11.33] Supports laravel/serializable-closure 2
https://github.com/laravel/framework/pull/53552
Последний PR в этом дайджесте Laravel добавляет поддержку Serializable замыканий версии 2.
Обсудить новости можно в чате по Laravel - https://t.me/laravel_chat
Symfony дайджест
По традиции я, Кирилл Несмеянов, продолжаю вас знакомить с самым лучшим, самым удобным, мощным, гибким и элегантным фреймворком тысячелетия - Symfony.
Прошёл ноябрь, а это значит, что на дворе релиз версии 7.2. Что ещё нового он нам подготовил? Давайте смотреть…
Консольный индикатор завершения прогресса
Компонент консоли - один из самых популярнейших, даже вне экосистемы Symfony. Его используют более 11000 OSS проектов, а количество загрузок приближается к одному миллиарду.
В Symfony 7.2 был добавлен индикатор завершения для прогресса, чтобы дать возможность сообщить пользователю о состоянии выполнения задачи. По умолчанию индикатор выводит вращающуюся текстовую "палку":
Конечно, этот индикатор можно кастомизировать, изменив анимацию, однако основная проблема состоит в том, что после завершения работы индикатор просто "повисает".
В Symfony 7.2 был добавлен индикатор завершения для прогресса, отображающий ✔ (галочку) в конце работы.
Очевидно, вы можете модифицировать на свой вкус это поведение, используя аргумент finishedIndicatorValue
.
use Symfony\Component\Console\Helper\ProgressIndicator;
$indicator = new ProgressIndicator($output, finishedIndicatorValue: '✅');
try {
$indicator->finish('Усё, шеф!');
} catch (\Throwable) {
$indicator->finish('Насяйника, оно бжумк!', '?');
}
Источник: symfony.com
Валидатор
Улучшения валидатора в Symfony 7.2. Казалось бы, куда дальше, но вот так бывает.
Улучшения проверки Bic
Для начала стоит заметить, что BIC - довольно специфичная штука, описанная стандартом ISO-9362. Отвечает за спецификацию метода идентификации участников финансовых расчётов. Основан на работах компании SWIFT для идентификации банков в сети SWIFT.
Каждый код BIC представлен в виде цифро-буквенной комбинации из 8 или 11 символов в верхнем регистре. Это всё, что следует знать, думаю, чтобы не углубляться в детали.
В любом случае, пользователи - ленивые существа, поэтому в большинстве случаев плюют на спецификацию и вводят данный код в произвольном регистре. Так что в Symfony 7.2 в данный валидатор было добавлено два режима работы:
strict
- (режим по умолчанию) строгая проверка согласно стандарту.case-insensitive
- регистронезависимая проверка кода.
use Symfony\Component\Validator\Constraints as Assert;
final readonly class TransactionRequestDTO
{
public function __construct(
#[Assert\Bic(mode: 'case-insensitive')]
public string $bic,
// ...
) {}
}
Улучшения проверки Unique
Валидатор Unique
проверяет, что все элементы коллекции уникальны в рамках этой коллекции. Однако, когда случается ошибка валидации - ошибка возникает для всего элемента, а не конкретного поля элемента.
В Symfony 7.2 был добавлен аргумент errorPath
, позволяющий указать конкретное поле элемента в котором произошла ошибка валидации.
use Symfony\Component\Validator\Constraints as Assert;
final readonly class MessageRequestDTO
{
public function __construct(
// ...
public string|int $id,
// ...
) {}
public static function getUniqueKey(self $dto): string
{
return (string) $dto->id;
}
}
final readonly class BatchMessageUpdateRequestDTO
{
public function __construct(
/**
* @var iterable<array-key, MessageRequestDTO>
*/
#[Assert\Unique(
normalizer: [MessageRequestDTO::class, 'getKeyForUniqueConstraint'],
errorPath: 'id'
)]
public iterable $messages,
) {}
}
Улучшения проверки Ulid
Symfony предоставляет отдельный компонент для генерации идентификаторов, где в качестве одного из доступных вариантов выступает идентификатор ULID.
Такой идентификатор может быть преобразован в несколько разных форматов, начиная от бинарного, заканчивая форматом RFC-4122.
use Symfony\Component\Uid\Ulid;
$ulid = Ulid::fromString('01E439TP9XJZ9RPFH3T1PYBCR8');
$ulid->toBinary(); // string(16) "\x01\x71\x06\x9d\x59\x3d\x97\xd3\x8b\x3e\x23\xd0\x6d\xe5\xb3\x08"
$ulid->toBase32(); // string(26) "01E439TP9XJZ9RPFH3T1PYBCR8"
$ulid->toBase58(); // string(22) "1BKocMc5BnrVcuq2ti4Eqm"
$ulid->toRfc4122(); // string(36) "0171069d-593d-97d3-8b3e-23d06de5b308"
$ulid->toHex(); // string(34) "0x0171069d593d97d38b3e23d06de5b308"
При этом, во время использования правил валидации Ulid его можно было передавать только в base32 формате. В Symfony 7.2 был добавлен аргумент format
у этой проверки, позволяющей указывать любой из доступных форматов.
use Symfony\Component\Validator\Constraints as Assert;
final readonly class MessageRequestDTO
{
public function __construct(
#[Assert\Ulid(format: Ulid::FORMAT_RFC4122)]
public string $id,
// ...
) {}
}
Улучшения зависимых проверок When
Проверка When
позволяет применить правило только если выражение возвращает true
. В Symfony 7.2, помимо передаваемого аргумента this
внутрь выражения был добавлен аргумент context
, содержащий весь контекст выполнения правила.
use Symfony\Component\Validator\Constraints as Assert;
final readonly class TransactionRequestDTO
{
public function __construct(
#[Assert\When(
expression: 'this.getType() == "percent" && context.getRoot().ok === true',
constraints: [
// ...
],
)]
public ?int $value,
) {}
public function getType(): string { ... }
}
Источник: symfony.com
Упрощения настроек доверенных прокси
В том случае если приложение Symfony под балансером или обратным прокси, то следует указать Symfony каким IP-адресам обратного прокси следует доверять. Если адрес обратного прокси-сервера постоянно ротируется, то следует настроить диапазон соответствующих адресов.
В Symfony 7.2 была добавлено сокращение PRIVATE_SUBNETS
для следующих значений:
127.0.0.0/8
- RFC1700 (Loopback)10.0.0.0/8
- RFC1918192.168.0.0/16
- RFC1918172.16.0.0/12
- RFC1918169.254.0.0/16
- RFC39270.0.0.0/8
- RFC5735240.0.0.0/4
- RFC1112::1/128
- Loopbackfc00::/7
- Unique Local Addressfe80::/10
- Link Local Address::ffff:0:0/96
- IPv4 translations::/128
- Unspecified address
Все значения также доступны в виде константы Symfony\Component\HttpFoundation\IpUtils::PRIVATE_SUBNETS
. В любом случае, если указать сокращение PRIVATE_SUBNETS
, то такую конфигурацию легче читать и поддерживать. Так что перечислять целый список более не требуется.
# config/packages/framework.yaml
framework:
trusted_proxies: '127.0.0.1,PRIVATE_SUBNETS'
Использование ENV-переменных для конфигурации доверенных прокси
Конфигурация доверенных прокси в настоящее время выполняется в файлах конфигурации с помощью ключей trusted_proxies
, trusted_headers
, trusted_hosts
и trust_x_sendfile_type_header
.
# config/packages/framework.yaml
framework:
# ...
trusted_proxies: '192.0.0.1,10.0.0.0/8'
trusted_headers: [ 'x-forwarded-for', 'x-forwarded-host', ... ]
trusted_hosts: [ '...' ]
trust_x_sendfile_type_header: true
В Symfony 7.2 аналогичную настройку теперь можно выполнять с помощью переменных окружения, вместо отдельной настройки конфигурации.
SYMFONY_TRUSTED_PROXIES
SYMFONY_TRUSTED_HEADERS
SYMFONY_TRUSTED_HOSTS
SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER
Источник: symfony.com
Упрощение однофайловых приложений
Использование однофайловых приложений, вместо классического скелетона - довольно редкая практика, однако идеально подходит для микрофреймворков, которым требуется исключительно роутер.
Создать такое приложение можно, используя всего два компонента:
symfony/framework-bundle
, включающего в себя основные компоненты ядра и зависимости на HTTP-кернел, конфигурацию, роутер и параметры окружения.symfony/runtime
, включающего в себя рантайм-мост под различные SAPI, начиная с классического PHP-FPM и заканчивая Swoole, RoadRunner, ReactPHP, PHP-PM, Workerman, Franken, Bref, Google Cloud и прочими...
composer symfony/framework-bundle symfony/runtime
Улучшения в Symfony 7.2 коснулись и его, позволяя не указывать явно список бандлов и конфигурацию. Так что всё приложение на Symfony теперь можно сократить до пары десятков строк кода:
// index.php
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Attribute\Route;
require __DIR__ . '/vendor/autoload.php';
final class Kernel extends BaseKernel
{
use MicroKernelTrait;
#[Route('/random/{limit}', name: 'random_number')]
public function __invoke(int $limit): JsonResponse
{
return new JsonResponse([
'number' => \random_int(0, $limit),
]);
}
}
return static function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
}
Изменения в Symfony 7.2 коснулись не только упрощения настройки кернела, позволяя делать большинство настроек опциональными, но и других интересных "мелочей":
Директория
config
с конфигурацией теперь опциональная. Конфигурацию, если она требуется, можно определить в методеconfigureContainer
.Файл
bundles.php
теперь тоже опциональный, можно использовать методregisterBundles
для дополнения списка бандлов. В стандартной поставке там всего одинFrameworkBundle
.
Ну и небольшая мелочь, которая, кажется, не совсем касается конкретно этих изменений, однако упомянуть её смысл имеет: теперь сервисы из DI-контейнера успешно внедряются в __invoke
-методы контроллеров. Ранее, если не путаю, требовалось написать свой собственный ValueResolver
. Но это не точно.
Источник: symfony.com
Новые опции команд
В Symfony 7.2 были улучшены многие существующие команды, добавлены новые опции и функции.
Переменные окружения в линтинге контейнера
В Symfony есть такая замечательная команда lint:container
, которая запускает проверку конфигурации контейнера: Проверяет что все сервисы корректно определены, все их зависимости корректно настроены, аргументы переданы, типы соответствуют и т.д. Однако эта команда не проверяет наличие переменных окружений или их значений по умолчанию, которые потом используются в сервисах.
Так что в Symfony 7.2 была добавлена опция --resolve-env-vars
, которая дополнительно проверяет, что все переменные окружения на месте и настроены.
php bin/console lint:container --resolve-env-vars --env=prod
Различные форматы статистики очередей
Команда messenger:stats
отображает информацию о количестве сообщений в очереди. В отличие от других команд, она не предоставляла различных форматов вывода. В Symfony 7.2 для статистики были добавлены форматы json
и text
.
Отличный функционал для агрегации данных с помощью внешних систем для вывода, например в Графану.
php bin/console messenger:stats --format=json
php bin/console messenger:stats my_transport_name other_transport_name --format=json
Код выхода при расшифровке "секретов"
В Symfony существует функционал, позволяющий шифровать переменные окружения, которые не допускаются в открытом виде. Этот функционал требуется для того, например, чтобы данные доступа к БД, ключи АПИ и прочие данные случайно не "засветились" где-либо в логах, данных трассировки и прочих местах. Конечно, функционал не решает всех возможных проблем, однако он упрощает их решение.
В любом случае, ранее, при использовании команды secrets:decrypt-to-local
, которая расшифровывала все "секреты" с помощью ключа и сохраняла их в локальном хранилище, при наличии ошибок расшифровки можно было получить код выхода 0
.
Теперь при наличии каких-либо проблем при декодировании код выхода будет всегда равен 1
, что позволяет проще отловить проблемы.
Пропуск неудачных сообщений из очереди
Команда messenger:failed:retry
позволяет выводить и повторять некорректно обработанные сообщения. Для каждого сообщения она предоставляет два варианта:
Повторить его прямо сейчас.
Удалить сообщение из очереди.
Однако, иногда требуется не удалять это сообщение когда вы не уверены, стоит ли повторять попытку обработки прямо сейчас.
В Symfony 7.2 был добавлен третий вариант: можно пропустить обработку сообщения сейчас, чтобы позже решить, что с ним делать.
Удобно, если вы внезапно обнаружили баг в проде и пока в спешке готовите хотфикс для него - не останавливаете работу консамеров.
Фильтрация отладки ассетов
Команда debug:asset-map
позволяет отображать подробную информацию о путях AssetMapper
, префиксах пространств имен, логических путях и так далее.
По мере роста приложения вывод этой команды становится все менее управляемым и увидеть портянку сообщений из сотен бандлов, разбираясь в ней не очень приятно.
Конечно, grep
спасает, однако это не совсем корректное и "легальное" решение проблемы, так что в Symfony 7.2 добавили более "умную" фильтрацию вывода.
# Можно указать имя ресурса или директорию, чтобы отобразить только
# результаты, которые ему соответствуют.
php bin/console debug:asset-map bootstrap.js
php bin/console debug:asset-map style/
# Можно указать расширение, чтобы отобразить только этот тип файла.
php bin/console debug:asset-map --ext=css
# Можно вывести только ресурсы в каталоге "vendor/" или наоборот,
# исключить любые результаты из него.
php bin/console debug:asset-map --vendor
php bin/console debug:asset-map --no-vendor
# Можно комбинировать все фильтры. Например, найти полужирные
# веб-шрифты в своих собственных (не вендорных) директориях.
php bin/console debug:asset-map bold --no-vendor --ext=woff2
Источник: symfony.com
Переработанный TypeInfo компонент
Symfony (и semver в частности) гарантирует, что любые минорные обновления ничего не "сломают" и версия 7.1 будет обратно совместима с версией 7.0. Очевидно, что и версия 7.2 так же обратна совместима с версией 7.1. Ну по крайней мере ничего критического не должно произойти.
Так же, Symfony поставляет некоторый набор функционала как "экспериментальный". Это набор функционала, которые находятся на раннем, скажем так, "бета-тесте" и могут изменить свой API и поведение на основе вашего, пользовательского опыта и фидбека.
Короче, хватит тянуть гуся за лапку... В общем, с прискорбием сообщаем, что TypeInfo, который был добавлен в Symfony 7.1 как экспериментальный - "всё". Ну как "всё". Старое API компонента - "всё" и изменения, коснувшиеся его, чуть-чуть ломают эту самую обратную совместимость.
После нескольких месяцев использования компонента в реальных приложениях и интеграции его в другие пакеты и библиотеки - компонент было решено чуть-чуть до(пере)работать.
Добавлен
CompositeTypeInterface
иWrappingTypeInterface
для лучшего описания того, состоит ли тип из нескольких типов, т.е. композитный или представляет собой просто декоратор одного;Добавлен
NullableType
, который расширяетUnionType
и при этом являетсяWrappingTypeInterface
, что значительно упрощает распознавание типа, допускающего значениеnull
, и получение связанного с ним типа, не допускающего значениеnull
;Удален магический
__call()
метод, которыйCollectionType
иGenericType
использовали для пересылки методов в их "обернутый" (врапнутый) тип. Удалено, поскольку теперь проще узнать, "оборачивает" ли один тип другой;Были переименованы все
isXxx()
(является) вsatisfyXxxx()
(удовлетворяет) и добавлены соответствующие методыCompositeTypeInterface::composedTypesSatisfy()
иWrappingTypeInterface::wrappedTypeSatisfy()
Ну и добавлено множество проверок для предотвращения недопустимых конструкций типов.
Как заявляют core-контрибьюторы Symfony - они теперь, цитата:
"Уверены, что компонент имеет правильную архитектуру и поведение. Вот почему мы больше не считаем его экспериментальной функцией, и вы можете спокойно использовать его, зная, что мы не внесем в него критических изменений."
Ну что ж, поживём - увидим!
Сериализатор
Мой любимый компонент! Сколько человеко-часов было потрачено на исправление изначально криво написанного и полу рабочего компонента - не счесть...
Поддержка потомков DateTime
В текущем нормалайзере для дат DateTimeNormalizer
не было никакой поддержки потомков классов дат. Ну вот так, все пакеты, вроде Carbon и Chronos шли лесом и просто не работали. В Symfony 7.2 данное поведение было поправлено и теперь можно сериализовать любых потомков DateTimeInterface
.
Конвертация из имён из snake_case
В сериализаторе уже ранее была поддержка автоматического преобразования имён из camelCase
в snake_case
. В Symfony 7.2 был добавлен класс SnakeCaseToCamelCaseNameConverter
, который обеспечивает обратную конвертацию из snake_case
в camelCase
.
Новые константы UUID
В текущем нормалайзере UidNormalizer
уже присутсвует набор констант для различных форматов вывода идентификаторов (NORMALIZATION_FORMAT_*
).
В Symfony 7.2 добавили новую константу UidNormalizer::NORMALIZATION_FORMAT_RFC9562
для вывода идентификаторов в формате RFC-9562. Кажется, теперь сериализация идентификаторов возможна в любых доступных форматах.
Удаление зависимости на сериализатор из Webhook
Компонент Webhook
использует сериализатор для преобразования данных в JSON. Это жестко "зашитая" зависимость, которую обычно избегают в компонентах Symfony, насколько это возможно.
В Symfony 7.2 эта зависимость теперь не обязательна. Если сериализатор не установлен, то компонент Webhook
будет использовать json_encode()
функцию, вместо сериализатора.
Источник: symfony.com
Стейтлес CSRF
Symfony 7.2 добавляет значительное улучшение функций безопасности: защита от CSRF (подделка межсайтовых запросов) без сохранения состояния.
Этот функционал использует комбинацию данных cookie
и других заголовков HTTP для проверки непостоянных (ну т.е. не сохраняемых, non-persistent) токенов.
Отличие этой функции по сравнению с традиционными методами CSRF защиты заключается в том, что токены проверяются без использования сессий на стороне сервера. Это делает его доступным для приложений, использующих HTTP-кэширование.
Кроме того, отсутствие использования сессий гарантирует, что пользователи не потеряют данные, если потратят слишком много времени на отправку формы.
Например, если сессия будет уничтожена во время заполнения формы, то функция «Запомнить меня» восстановит состояние, и отправленная форма всё равно будет принята.
Включение стейтлес CSRF
Для включения стейтлес CSRF в вашем проекте следует добавить несколько опций в настройках:
# config/packages/framework.yaml
framework:
csrf_protection:
stateless_token_ids: ['my_stateless_token_id']
Опция stateless_token_ids
обеспечивает "безопасность" этого функционала CSRF, так же как и традиционная функция CSRF, поскольку она явно перечисляет идентификаторы токенов, разрешенные при использовании новой функции.
Помимо этого добавлены некоторые другие настройки секции csrf_protection
:
cookie_name
- Имя используемогоcookie
(по умолчанию:csrf-token
);check_header
- В том случае если значение содержитtrue
, то CSRF-токен проверяется в заголовке HTTP в дополнение кcookie
(по умолчанию:false
).
Как это работает?
Сначала проверяются HTTP-заголовки Origin
и Referer
на соответствие истории запроса. Поведение основано на возможности приложения определить этот источник. Если же приложение находится за реверс-прокси, то следует забыть настроить его для отправки соответствующих HTTP-заголовков X-Forwarded-*
и Forwarded
.
Затем этот запрос проверяется с помощью cookie
и CsrfToken
. Если cookie
найден, он должен содержать то же значение, что и CsrfToken
. За такую "двойную отправку" может отвечать, например JavaScript. Помимо этого значение токена должно быть сгенерировано заново при каждом запросе (т.е. при отправке формы) с использованием криптографически безопасного ГПСЧ (генератора псевдослучайных чисел, PRNG).
Если же механизм двойной отправки не сработал, или отсутствуют HTTP-заголовки Origin
и Referer
, то скорее всего указывает на то, что JavaScript отключен на стороне клиента (ну или некорректно реализован), или заголовки Origin
и Referer
были отфильтрованы.
Такие запросы, в которых отсутствует как двойная отправка, так и информация об источнике, считаются небезопасными.
Если вы устанавливаете приложение "с нуля", то эта функция CSRF будет включена по умолчанию. Symfony Flex автоматом добавит следующую конфигурацию:
# config/packages/framework.yaml
framework:
form:
csrf_protection:
token_id: 'submit'
csrf_protection:
stateless_token_ids: [ 'submit', 'authenticate', 'logout' ]
Источник: symfony.com
Устаревший функционал
Каждый минорный релиз сопровождается сообщениями об устаревании какого-либо старого функционала. Данный функционал в будущем будет удалён в очередном мажорном релизе. Релиз Symfony 7.2 не стал исключением из этого списка и включил в себя некоторый функционал, который будет удалён через год с релизом версии 8.0.
Опции конфигурации ID сессий
Сам язык PHP уже включает в себя функционал сессий и возможность их настройки в php.ini
с помощью опций session.sid_length
и session.sid_bits_per_character
. В PHP 8.4 данные опции были помечены как устаревшие, т.к. пользователи могли выставлять слишком короткие и небезопасные значения или наоборот, выставлять слишком длинные значения, которые лишь нагружают процессор, но при этом не сильно влияют на безопасность. В любом случае, особого смысла их отдельно настраивать нет.
В Symfony в настройках сессий так же можно было использовать подобный функционал в секции framework.session
конфигурации сессий и в версии 7.2 он так же был помечен как устаревший.
Настройки сборщика мусора по умолчанию
При запуске сессий PHP вызывает обработчик сборщика мусора случайным образом на основе вероятности, определяемой php.ini
в настройках session.gc_probability
и session.gc_divisor
. Вероятность определяется соотношением значения вероятности (probability) к делителю (divisor). Например, значения 5 и 100 означают соотношение 5 к 100, что равняется 5% вероятности вызова сборщика мусора.
В Symfony опция session.gc_probability
выставлялась явно и имела значение по умолчанию 1. Это значение переопределяло соответствующую настройку php.ini
, что не совсем очевидно. В Symfony 7.2 это значение по умолчанию было удалено и теперь по умолчанию будут использоваться настройки из php.ini
.
Но в любом случае механизм сессий по-умолчанию, встроенный в PHP, довольно ограничен, так что всё равно рекомендуется использовать собственный обработчик или те "драйвера", которые поставляются вместе с Symfony.
Другие опции сессий
Помимо вышеперечисленного, в PHP 8.4 были помечены как устаревшие и другие настройки сессий. Соответственно, при использовании драйвера NativeSessionStorage
теперь не рекомендуется использовать следующие связанные настройки:
referer_check
- использовался для проверки заголовкаReferer
на наличие подстроки, определенной в этой опции. Если данные не совпадали, то сессия считалась недействительной.use_only_cookies
- указывало на то, будет ли расширение сессий использовать только заголовокcookie
для хранения идентификатора сессий на стороне клиента.use_trans_sid
- в документации по PHP сказано, что эта опция отвечала за, цитата: "прозрачную поддержку sid". Это довольно старый функционал, который вместо хранения идентификаторов сессий в cookie использовал query-аргументы для передачи ID сессий, вида:site.ru?PHPSESSID=DEADBEEF
. Соответственно, данный функционал модифицировал отдаваемый HTML "на-лету", добавляя аргументPHPSESSID
в каждую ссылку.trans_sid_hosts
- задавала хосты для перезаписи для включения идентификатора сессии при включении функционала "прозрачных sid". То есть, если не путаю, проверяла ссылки на соответствие указанным адресам и только тогда их модифицировала.trans_sid_tags
- содержала HTML-теги в которых будет автоматом вставляться идентификатор сессии. Формат тегов был вида "название тега" + "=" + "атрибут тега":a=href,area=href,frame=src,input=src,form=
, гдеform
- специальный тег, добавляющий внутрь<input hidden="session_id" name="session_name">
.
Очевидно, что это какой-то древний набор "костылей и велосипедов". Не удивительно, что его удалили из языка. Ну и понятно почему удалили из Symfony вслед за PHP 8.4.
Пустые идентификаторы пользователей
Раннее, интерфейс UserInterface::getUserIdentifier()
возвращал просто строку. Это не совсем корректно с точки зрения семантики и вообще здравой логики. Поэтому в Symfony 7.2 этот метод должен возвращать "непустую строку".
Некоторые аутентификаторы, такие как FormLoginAuthenticator
и JsonLoginAuthenticator
, уже обновлены в соответствии с этим поведением и не позволяют больше идентификаторам пользователей быть пустыми, проверяя это поведение.
Вообще, в некотором роде это разрыв обратной совместимости, однако, кажется, вряд ли найдётся кто-то, кто зачем-то использовал данный функционал.
Использование "!tagged" тега
Данное изменение касается DI-контейнера. Раньше для передачи сервисов, помеченных тегом можно было использовать как !tagged
, так и !tagged_iterator
.
Если не путаю, то это было псевдонимами, однако использование !tagged
не совсем очевидно, т.к. помимо !tagged_iterator
есть ещё и !tagged_locator
, например, поэтому его объявили устаревшим в Symfony 7.2.
Так что теперь желательно избавиться от этого псевдонима и указывать !tagged_iterator
явно.
services:
App\ExampleFactory:
arguments:
# Внедряем все драйвера, помеченные тегом "app.example_driver"
# в аргумент "$drivers" сервиса "App\ExampleFactory"
$drivers: !tagged_iterator 'app.example_driver'
Источник: symfony.com
Изменения framework.secret опции
Одним из довольно известных параметров конфигурации Symfony является secret
, который можно настроить с помощью параметра framework.secret
или APP_SECRET
переменной окружения в одном из файлов .env
. В конечном итоге само значение передаётся в параметр kernel.secret
в приложении, вне зависимости от того как оно было настроено.
В любом случае, данная опция больше не нужна в приложениях Symfony 7.2 и требуется только в дополнительном функционале, который её использует, так что настраивать её отдельно в явном виде больше не требуется.
Естественно, настройка потребуется если вы используете, например:
Ссылки для входа в систему без пароля;
Функционал «Запомнить Меня» (Remember Me) для автоматического входа в систему на основе предыдущих сеансов;
Ограничитель скорости (Rate Limiter) для контроля частоты определенных действий;
ESI для подключаемого контента при использовании HTTP-кэширования.
В том случае, если вы создаёте новое приложение на Symfony 7.2, то значение "секрета" будет теперь пустым по умолчанию. В том же случае, если вы решите использовать какой-либо функционал, требующий этот "секрет", то будет выброшено соответствующее исключение, если значение "секрета" не будет задано.
На всякий случай хочу напомнить, что Symfony по умолчанию использует довольно небезопасную и странную схему с "коммитами .env
в Git", которые содержат значения по умолчанию, а конкретные значения под определённое окружение допускались в .env.dev
, .env.prod
, .env.test
и проч.
Схема работы довольно бессмысленная и я пока не встречал её использование в реальной продуктовой разработке. Так или иначе, каждый её переделывал на более удобный вариант с игнором любого .env
файла, где сам файл .env
и был точкой истинности для определённого окружения, и выставляется (создаётся) отдельно на каждом из окружении руками.
Так вот, оказывается что кто-то использует эту схему по умолчанию. Поэтому изменения коснулись и этого поведения во время локальной разработки Symfony, где фреймворк генерировал этот секрет в файле .env
, что могло приводить к проблемам с безопасностью (т.к. .env
коммитился в Git). Теперь значение будет генерироваться в файле .env.dev
, что поможет избежать проблем, когда "секрет" был общим для локального и боевого стендов.
Ну в общем, всё по классике. Сами создаём себе проблемы - сами героически решаем.
Источник: symfony.com
Обсудить новости можно в чате по Symfony - https://t.me/symfony_cutcode
Видео версия дайджеста
Комментарии (5)
zorn-v100500
09.12.2024 12:12Схема работы довольно бессмысленная и я пока не встречал её использование в реальной продуктовой разработке. Так или иначе, каждый её переделывал на более удобный вариант с игнором любого
.env
файла, где сам файл.env
и был точкой истинности для определённого окружения, и выставляется (создаётся) отдельно на каждом из окружении руками.Очень даже удобно, когда в `.env` видно какие локальные настройки можно переопределить. Если открыть хоть через 10 лет, не надо будет по всему коду искать, как же эта переменная называлась.
А настоящие настройки храню в `.env.local`, так что с "каждый её переделывал" вы погорячились ;)
Более того, я подобную схему использую не только в PHP - в vuejs тоже что то подобное по умолчанию
SerafimArts
09.12.2024 12:12Согласен, чуть-чуть чувствуется что перегнул с иронией.
Однако не поверите - реально на каждом проекте (говорю про 3 разных, начиная с симфы 3.4, заканчивая 7.х) видел другую, где всё ровно наоборот:
.env.example
как шаблон, а.env
уже реальные значения под гитигнором. Точно такая же схема и в ларке идёт по умолчанию. А оригинальную симфонёвую схему ни разу не встречал.zorn-v100500
09.12.2024 12:12Ну с лары наверное эта привычка и перекочевала. Только непонятно зачем себе усложнять жизнь - symfony flex например пишет в
.env
умолчания с маркерами (чтобы почистить при удалении пакета) если есть в рецепте.SerafimArts
09.12.2024 12:12Ну не только лара, ещё раньше докер подхватывал env файл по дефолту в какой-то версии, а потом заменили на явное указание
env_file
секции сrequire: false
, решения под ноду ещё. Рекомендации в Rails такие же. Да куча, подозреваю, решений, где рекомендуется именно использование файла.env
для текущего окружения, а не его оверрайд.Единственное исключение, которое я встречал (помимо дефолтов Symfony) - это Sentry, там
.env
по-дефолту, а для оверрайда предлагают использовать.env.custom
.Но в любом случае, по-мне, это примерно тоже самое как поставлять
php.ini
в качестве шаблона, а чтоб переопределить - надо создать файлphp.local.ini
. Странное решение.P.S. Я уже признался, что погорячился. Можно списать на то, что автор дайджеста по симфе -- токсик и просто хейтит "непривычные для него решения")
psman
В текущий реалиях новости о Шторме как то кажутся чуточку бесполезными после аннулирование купленных ранее лицензий.