В Laravel 13.23 добавлен драйвер журнала   ежемесячно  , который меняет один файл в календарный месяц, выбор клиента для каждого сообщения для почтового транспорта SES v2, поддержку предложения PostgreSQL   USING   при изменении типов столбцов и безопасное по времени сравнение для секрета обхода режима обслуживания. Команда Laravel выпустила версию 13.23.0 27 июля 2026 года.

  • Новый драйвер журнала   ежемесячно   и соответствующий канал журнала   ежемесячно  
  • Поддержка заголовка   X-SES-TENANT-NAME   в транспорте SES v2.
  •  ->using()  о  ->change()  миграциях для приведения типов PostgreSQL
  • Сравнение секретов обхода режима обслуживания с сохранением времени
  • Переписанный конвертер часовых поясов  schedule:list  и различные исправления.

# Что нового

# Драйвер ежемесячного журнала

Драйвер   daily   чередует файл в день, что подходит приложениям, которые записывают достаточно данных для заполнения одного файла. Вместо этого приложения меньшего размера получают каталог с почти пустыми файлами. Новый драйвер   ежемесячно   находится между   одиночным   и   ежедневным   , записывая один файл за календарный месяц (  laravel-2026-07.log  ,  laravel-2026-08.log  и т. д.):

'monthly' => [
'driver' => 'monthly',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'max_files' => 3,
'replace_placeholders' => true,
],
 

Оба вращающихся драйвера теперь используют метод  createRotatingDriver()  для  LogManager  и различаются только передаваемым ими форматом даты Monolog ( FILE_PER_DAY  вместо  FILE_PER_MONTH ) и стандартным сроком хранения: 14 файлов для   ежедневно   , 3 для   ежемесячно   .

Хранение также имеет новый ключ конфигурации. Канал   daily   сначала считывает  max_files  и возвращается к существующему ключу   дней  , поэтому приложения, которые уже установили   дней  , продолжают работать, а переменная среды  LOG_DAILY_DAYS  остается неизменной. В поставляемой конфигурации теперь используется  max_files  для обоих каналов. Предоставлено @SjorsO в #60892 , канал конфигурации добавлен @yazansalhi в #60895 .

# Поддержка арендатора SES в транспортном средстве SES v2

Арендаторы Amazon SES изолируют репутацию и конфигурацию отправки между группами отправителей. До сих пор транспорт мог работать только с арендатором, настроенным для всего приложения. Транспорт SES v2 теперь считывает заголовок   X-SES-TENANT-NAME   из исходящего сообщения и пересылает его как параметр  TenantName  при вызове SDK, следуя шаблону, уже использованному для   X-SES-LIST-MANAGEMENT-OPTIONS   .

Вы можете установить заголовок из почтового сообщения:

use Illuminate\Mail\Mailables\Headers;
 
public function headers(): Headers
{
return new Headers(text: ['X-SES-TENANT-NAME' => 'tenant-id']);
}
 

Или из почтового сообщения с уведомлением:

return (new MailMessage)->withSymfonyMessage(function ($message) {
$message->getHeaders()->addTextHeader('X-SES-TENANT-NAME', 'tenant-id');
});
 

Если заголовок отсутствует, отправка осуществляется точно так же, как и раньше, и этот параметр опускается в версиях AWS SDK, предшествующих этой функции. Предоставлено @atymic в #60886 .

#  ->using()  для изменений столбца PostgreSQL

PostgreSQL отказывается изменить столбец на несовместимый тип без явного приведения, возвращая ошибку типа   "имя столбца" не может быть автоматически приведено к типу date   . Не было возможности передать это преобразование через построитель схемы, поэтому миграции, написанные для MySQL или SQLite, в PostgreSQL завершались неудачно. Определения столбцов теперь принимают предложение  using() :

Schema::table('currency_rates', function (Blueprint $table) {
$table->date('name')->using('name::date')->change();
});
 

Это предложение применимо только к PostgreSQL; другие грамматики игнорируют это. Предоставлено @shemgp в #60901 .

# Секрет обхода режима обслуживания с сохранением времени

Поток обхода режима обслуживания сравнивает секрет двумя разными способами:  hash_equals()  при проверке входящего секрета, простой  ===  в промежуточном программном обеспечении  PreventRequestsDuringMaintenance  и путь начальной загрузки   Maintenance-mode.stub  . В обоих сравнениях теперь используется  hash_equals()  , а защита промежуточного программного обеспечения  isset()  была заменена на  is_string() , поэтому нестроковое значение файла cookie не может вызвать  TypeError  .

Это изменение глубокоэшелонированной защиты, а не исправление уязвимой ошибки, поскольку предположение о неправильной длине никогда не достигает побайтового сравнения. Поведение для допустимого ввода не изменилось. Предоставлено @yazansalhi в #60896 .

# Другие исправления и улучшения

  • Конвертер часовых поясов  schedule:list  был переписан. Раньше он сдвигал только простые числовые поля cron, оставляя нетронутыми диапазоны (  8-23  ), шаги (  */2  ) и подстановочные знаки, но при этом помечал выходные данные преобразованным часовым поясом. Теперь оно расширяет каждое поле, сдвигает значения и сжимает их обратно в компактный синтаксис, разбивая событие на несколько записей, когда сдвинутый диапазон пересекает полночь относительно поля фиксированного дня. Он также учитывает переходы на летнее время и дробные смещения, такие как UTC+5:45 ( #60877 ).
  • Сбои в журнале устаревания больше не перерастают в фатальные ошибки ( #60893 ).
  •  CookieJar::queued()  правильно возвращает предоставленное значение по умолчанию ( #60904 ), а  Arr::last()  обрабатывает   null   без ошибок ( #60887 )
  •  ImageManager::fromStorage()  принимает поддерживаемое перечисление в качестве имени диска ( #60889 ), а класс   Image   получил улучшения типа ( #60890 ).
  •  Collection::select()  имеет более точные общие типы для статического анализа ( #60898 ).
  • Предыдущее исправление для  ShouldBeUniqueUntilProcessing  заданий, принудительно снимающих блокировки, которыми они не владеют, было отменено ( #60905 ).

  Ссылки