В этом обзоре я собрал самые интересные для меня изменения в Drupal 11.3. Полный список обновлений вы найдёте на странице релиза1.
Одновременно с Drupal 11.3 состоялся релиз Drupal 10.6 — это последний минорный релиз для Drupal 10.
Скорее всего, в июне 2026 года (а если не успеют — то в декабре) Drupal 10 будет объявлен устаревшим. В это же время Drupal 11 перейдёт на долгосрочную поддержку (LTS), а также состоится релиз Drupal 12.
Если вы читали эту публикацию до релиза Drupal 11.3, то уже не найдёте раздел про новый экспериментальный модуль Symfony Mailer (mailer) — он не попал в релиз.2 Мы увидим его в Drupal 11.4 или даже в Drupal 12.
Добавлена поддержка ООП-хуков в темах
Теперь темы поддерживают ООП-хуки.3 Подход в целом такой же, как в модулях, но есть несколько особенностей:
- Параметры
$moduleи$orderатрибута#[Hook]не поддерживаются. Поэтому темы не могут регистрировать хуки от лица других модулей или тем. Точно так же модули не могут регистрировать хуки от лица какой-либо темы. - В темах недоступны атрибуты
#[ReorderHook](нельзя менять порядок выполнения хуков) и#[RemoveHook](нельзя удалять сторонние хуки). - Порядок выполнения хуков фиксированный: модули → базовая тема → основная тема.
- Для темы доступны только те альтер-хуки, которые проходят через
Drupal\Core\Theme\ThemeManagerInterface::alter()/::alterForTheme().
Представлен объектно-ориентированный API для работы с определёнными типами рендер-массивов
В ядре сделаны первые шаги по переводу рендер-массивов на объектно-ориентированный подход. Первыми элементами, переведёнными на ООП, стали:
Изменение позволяет работать с этими структурами как с классами. Это даёт возможность использовать преимущества ООП в IDE: подсказки и автодополнение. Полный отказ от рендер-массивов планируется к релизу Drupal 13.
Основная идея: сервис Drupal\Core\Render\ElementInfoManagerInterface теперь предоставляет структуры рендер-элементов. Они служат основой для форм и активно применяются при их создании.
Листинг 1. Пример использования сервиса Drupal\Core\Render\ElementInfoManagerInterface для работы с рендер-массивов.
use Drupal\Core\Render\Element\Details;
use Drupal\Core\Render\Element\Email;
use Drupal\Core\Render\Element\Submit;
use Drupal\Core\Render\Element\Textfield;
use Drupal\Core\Render\ElementInfoManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
$form_render_array = [
'foo' => [
'#type' => 'textfield',
],
'actions' => [
'#type' => 'actions',
],
];
$element_info_manager = $container->get(ElementInfoManagerInterface::class);
$form = $element_info_manager->fromRenderable($form_render_array);
$form->removeChild('foo');
$textfield = $element_info_manager->fromClass(Textfield::class);
$textfield->default_value = new TranslatableMarkup('Hello, World!');
$textfield->required = TRUE;
$form->addChild('bar', $textfield);
$email_wrapper = $form->createChild('email_wrapper', Details::class);
$email_wrapper->open = TRUE;
$email = $email_wrapper->createChild('email', Email::class);
$email->placeholder = new TranslatableMarkup('Enter your email address');
$email->required = TRUE;
$actions = $form->getChild('actions');
$submit = $actions->createChild('submit', Submit::class);
$submit->button_type = 'primary';
$submit->value = new TranslatableMarkup('Submit');
$form_render_array = $form->toRenderable();
Чтобы добавить необходимую информацию в собственные рендер-элементы, необходимо указывать свойства элемента в PHPDoc через @property-нотацию.
Листинг 2. Пример #[RenderElement]-плагина с описанием свойств через @property-нотацию:
use Drupal\Core\Render\Attribute\RenderElement;
use Drupal\Core\Render\Element\RenderElementBase;
/**
* @property string|null $my_property
* @property array<string> $another_property
*/
#[RenderElement('my_element')]
final class MyElement extends RenderElementBase {
public function getInfo(): array {
return [
'#my_property' => NULL,
'#another_property' => [],
];
}
}
Добавлена поддержка HTMX
В ядре началась работа по внедрению HTMX. Постепенно он заменит AJAX-подсистему и возьмёт на себя её функции. HTMX67 — декларативная система разметки, которая расширяет возможности HTML с помощью специальных атрибутов.
Основные изменения:
- Новый класс
Drupal\Core\Htmx\Htmx— для работы с атрибутами и заголовками HTMX.8 - Класс
Drupal\Core\Render\Hypermedia\HtmxLocationResponseData— для управления данными.8 - Поддержка HTMX-запросов в
FormBuilder.8 - Интеграция HTMX с AJAX-подсистемой.8
- Новый формат обёртки
drupal_htmx(Drupal\Core\Render\MainContent\HtmxRenderer) — для ответов с использованием HTMX.8 - Добавлена новая опция маршрута (
_htmx_route) — для маршрутов, обрабатывающих HTMX-запросы (drupal_htmx, упомянутый выше).9 - Добавлен новый трейт
Drupal\Core\Htmx\HtmxRequestInfoTrait.10 Он помогает классам формировать рендер-массивы для HTMX. - Форма
ConfigSingleExportFormтеперь динамически обновляет URL — это происходит в зависимости от её состояния.11
Пока у меня нет опыта работы с HTMX, поэтому сложно оценить его практическую пользу. Однако инструмент теоретически хорошо подходит для ленивой подгрузки тяжёлых данных с бэкенда. Принцип прост: когда элемент попадает в область видимости, он заменяется содержимым. Вся реализация остаётся на бэкенде — писать JavaScript не требуется.
Вот пример: обычная форма становится AJAX-формой. Валидация и отправка происходят без перезагрузки страницы. Если всё прошло успешно, форма заменяется сообщением об успешной отправке.
Листинг 5. Пример формы, отправка которой будет производиться при помощи HTMX
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Htmx\Htmx;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
final readonly class HtmxForm implements FormInterface {
public function getFormId(): string {
return 'example_htmx';
}
public function buildForm(array $form, FormStateInterface $form_state): array {
$form_url = Url::fromRoute('<current>', options: [
'query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_htmx'],
]);
(new Htmx())->post($form_url)->target('this')->swapOob(TRUE)->applyTo($form);
$form['status_messages'] = [
'#type' => 'status_messages',
'#weight' => -1000,
];
$form['email'] = [
'#type' => 'email',
'#title' => new TranslatableMarkup('Email address'),
'#required' => TRUE,
'#placeholder' => 'example@example.com',
];
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => new TranslatableMarkup('Submit'),
];
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state): void {
if ($form_state->getValue('email') === 'example@example.com') {
return;
}
$form_state->setError($form['email'], new TranslatableMarkup('You have entered the wrong email address.'));
}
public function submitForm(array &$form, FormStateInterface $form_state): void {
$form = [];
$form['success'] = [
'#theme' => 'status_messages',
'#message_list' => [
'status' => [
new TranslatableMarkup('Form successfully submitted.'),
],
],
'#status_headings' => [
'status' => new TranslatableMarkup('Success'),
],
];
}
}
Видео 1. Демонстрация формы с HTMX обёрткой
Добавлена встроенная CLI-утилита и API для экспорта содержимого в YAML-формате
Теперь можно экспортировать контентные сущности в YAML-формате с помощью встроенной CLI-утилиты и API.1213 Раньше для этого требовался сторонний модуль, например Default Content14.
Для экспорта конкретных сущностей используйте новую команду:
php web/core/scripts/drupal content:export <entity_type_id> <entity_id>
Функционал импорта доступен начиная с более ранних версий, но отдельной команды для него нет. Чтобы импортировать содержимое, нужно воспользоваться сервисом Drupal\Core\DefaultContent\Importer.
По умолчанию результат выводится в стандартный поток вывода (stdout). Чтобы сохранить его в файл, нужно использовать перенаправление.
Листинг 6. Пример использования команды content:export для экспорта содержимого в YAML-формате и результат выполнения.
php web/core/scripts/drupal content:export node 19 > node-19.yml
default:
revision_uid:
-
entity: ec6b7ed6-2a25-412b-884b-608493045a7f
status:
-
value: true
uid:
-
entity: ec6b7ed6-2a25-412b-884b-608493045a7f
title:
-
value: 'About Umami'
promote:
-
value: false
sticky:
-
value: false
moderation_state:
-
value: published
path:
-
alias: /about-umami
langcode: en
content_translation_source:
-
value: und
content_translation_outdated:
-
value: false
field_body:
-
value: '<p>Umami is a fictional food magazine that has been created to demonstrate how you might build a Drupal site using functionality provided ''out of the box''.</p><p>For more information visit <a href="https://www.drupal.org/docs/umami-drupal-demonstration-installation-profile">https://www.drupal.org/docs/umami-drupal-demonstration-installation-profile</a>.</p>'
format: basic_html
translations:
es:
revision_uid:
-
entity: ec6b7ed6-2a25-412b-884b-608493045a7f
status:
-
value: true
uid:
-
entity: ec6b7ed6-2a25-412b-884b-608493045a7f
title:
-
value: 'Acerca de Umami'
promote:
-
value: false
sticky:
-
value: false
revision_translation_affected:
-
value: true
moderation_state:
-
value: published
path:
-
alias: /acerca-de-umami
langcode: es
content_translation_source:
-
value: und
content_translation_outdated:
-
value: false
field_body:
-
value: '<p> Umami es una revista ficticia de alimentos que se ha creado para demostrar cómo se puede construir un sitio de Drupal con la funcionalidad que se proporciona ''fuera de la caja''. </p> <p> Para obtener más información, visite <a href="https://www.drupal.org/docs/umami-drupal-demonstration-installation-profile">https://www.drupal.org/docs/umami-drupal-demonstration-installation-profile</a>.</p> '
format: basic_html
_meta:
version: '1.0'
entity_type: node
uuid: 7889c36d-3ba6-4e32-8f15-d290925c46e0
bundle: page
default_langcode: en
depends:
ec6b7ed6-2a25-412b-884b-608493045a7f: user
Обратите внимание что created15 и некоторые другие поля по умолчанию не экспортируется.
Если вы хотите выгрузить данные в определённую директорию, используйте опцию --dir:
php web/core/scripts/drupal content:export node 123 --dir=content
Эта команда сохранит содержимое по пути content/node/{UUID}.yml. Опционально можно добавить опцию --with-dependencies, в таком случае будут также выгружены и все зависимости экспортируемой сущности в формате: {dir}/{entity_type_id}/{uuid}.yml.
Для программного экспорта данных используйте новый сервис Drupal\Core\DefaultContent\Exporter.
Листинг 7. Примеры использования сервиса Drupal\Core\DefaultContent\Exporter для программного экспорта содержимого.
use Drupal\Core\DefaultContent\Exporter;
use Drupal\node\Entity\Node;
$exporter = $container->get(Exporter::class);
// Экспорт в массив.
$content_structure = $exporter->export($entity_storage->load(19));
// Экспорт в директорию.
$exporter->exportToFile($entity_storage->load(19), 'content');
// Экспорт в директорию со всеми зависимостями.
$exporter->exportWithDependencies($entity_storage->load(19), 'content');
Также добавлено новое событие Drupal\Core\DefaultContent\PreExportEvent, позволяющее управлять экспортом данных. Оно даёт возможность:
- Настраивать включение/исключение ключей сущностей (
uuid,langcode,statusи т.д.) через метод::setEntityKeyExportable(); - Контролировать экспорт отдельных полей с помощью метода
::setExportable(); - Влиять на результат экспорта через функцию обратного вызова в методе
::setCallback(), который обрабатывает каждое значение поля отдельно.
Метод ::setCallback() работает в двух режимах:
- При совпадении параметра
$name_or_data_typeс названием поля — управляет экспортом конкретного поля; - При указании типа поля через префикс
field_item:(например,field_item:entity_reference) — применяет настройки ко всем полям этого типа.
Листинг 8. Пример использования события Drupal\Core\DefaultContent\PreExportEvent для управления данными при использовании программного выгрузки содержимого.
use Drupal\commerce_price\Plugin\Field\FieldType\PriceItem;
use Drupal\Core\DefaultContent\ExportMetadata;
use Drupal\Core\DefaultContent\PreExportEvent;
use Drupal\Core\Field\FieldItemInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
final readonly class FooSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [PreExportEvent::class => 'preExport'];
}
public function preExport(PreExportEvent $event): void {
$entity = $event->entity;
if ($entity->getEntityTypeId() !== 'commerce_product_variation') {
return;
}
// Обратите внимание, что здесь именно ключ, а не его синоним.
// @code
// …
// entity_keys: [
// 'owner' => 'uid',
// ],
// …
// @endcode
$event->setEntityKeyExportable('owner', FALSE);
// По умолчанию UUID скрывается.
$event->setEntityKeyExportable('uuid');
$event->setExportable('field_in_stock', FALSE);
$event->setExportable('field_primary_category');
$event->setCallback('field_item:commerce_price', $this->formatPrice(...));
}
private function formatPrice(FieldItemInterface $item, ExportMetadata $metadata): void {
\assert($item instanceof PriceItem);
return $item->get('formatted')->getValue();
}
}
Добавлен экспериментальный MySQLi драйвер для асинхронных запросов к базе данных
Добавлен экспериментальный драйвер MySQLi16 для MySQL/MariaDB, использующий PHP-расширение mysqli.17 Планируется задействовать Revolt18 PHP event loop для ускорения операций (например, загрузка сущностей с множеством полей, сложных представлений). Драйвер скрыт и предназначен только для тестирования.
Листинг 9. Настройка драйвера MySQLi в settings.php
$databases['default']['default'] = [
// Прочие настройки, такие как подключение к базе данных, неизменны.
'driver' => 'mysqli',
'namespace' => 'Drupal\\mysqli\\Driver\\Database\\mysqli',
'autoload' => 'core/modules/mysqli/src/Driver/Database/mysqli/',
'dependencies' => [
'mysql' => [
'namespace' => 'Drupal\\mysql',
'autoload' => 'core/modules/mysql/src/',
],
],
];
Если вдруг «зачесались» руки проверить «ускорение» — не тратьте время. Я уже всё проверил.
Грубый тест производительности с использованием demo_umami профиля (siege -c 10 -t 60s --no-parser --no-follow [URL] с отключёнными кешами: render, entity, data, page, dynamic_page_cache):
- Главная страница:
mysql→ 235 мсmysqli→ 283 мс- Итог: замедление ~20%
- Страница статей (
/en/articles):mysql→ 219 мсmysqli→ 260 мс- Итог: замедление ~18%
- Страница рецепта (
/en/recipes/borscht-with-pork-ribs):mysql→ 248 мсmysqli→ 320 мс- Итог: замедление ~29%
Я также скопировал 10 000 статей с переводами, затем измерил скорость загрузки всех сущностей (повторюсь — без кеша entity).
Листинг 10. Измерение производительности загрузки сущностей
use Drupal\Component\Utility\Timer;
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
$iterations = 10;
Timer::start('mysql-v-mysqli');
for ($i = 0; $i < $iterations; $i++) {
$container->get(EntityTypeManagerInterface::class)->getStorage('node')->loadMultiple();
$container->get(MemoryCacheInterface::class)->deleteAll();
}
Timer::stop('mysql-v-mysqli');
echo Timer::read('mysql-v-mysqli') / $iterations;
Для mysql время выполнения составило 1218 мс, а для mysqli — 1533 мс. Это замедление примерно на 25%.
Текущая версия драйвера — основа для будущих оптимизаций. Даже если использовать Event Loop для запросов в вашем коде, останется проблема множества запросов от Drupal и сторонних модулей. Пока зафиксируем добавление драйвера и подождём дополнительных улучшений.
Дополнительные улучшения и изменения
API и разработка
- Добавлен метод
::mergeWith()в классAjaxResponse. Он позволяет объединять AJAX-команды из разных экземпляровAjaxResponseв один ответ.19 - Добавлена поддержка сокращённого синтаксиса
@>вместо!service_closureв YAML-файлах сервисов.20 - Метод
EntityController::addPage()теперь ожидает новый параметр$request.21 - Для AJAX-представлений добавили команду, которая устанавливает URL в браузере без перезагрузки страницы.22
- Добавлено событие
WorkspaceSwitchEventдля реагирования на смену активного рабочего пространства.23 - Класс
Drupal\taxonomy\Form\OverviewTermsтеперь наследуется отDrupal\Core\Entity\EntityForm. Это улучшает расширяемость формы обзора терминов словарей.24 - Добавлены новые синонимы для маршрутов:
node.add→entity.node.add_formиnode.add_page→entity.node.add_page.25 Это сделано в рамках стандартизации генерации маршрутов. - Добавлена фабрика (сервис:
config.importer.factory) для создания объектаConfigImporter.26 - В интерфейс
Drupal\Core\State\StateInterfaceдобавлен метод::getValuesSetDuringRequest(). Он позволяет отследить, как менялись значения в пределах одного HTTP-запроса.27 - Batch API теперь использует
CallableResolverдля обработки функций обратного вызова.28 Это позволяет применять сервисы для операций пакетной обработки. - Form API теперь также использует
CallableResolverдля обработки функций обратного вызова.29 - В класс
Drupal\Component\Utility\SortArrayдобавлен статический метод::sortByKeyRecursive(). Он выполняет рекурсивную сортировку массивов по ключам.30 - Обработчики форм в определениях типов сущностей и маршрутах с параметром
_entity_formтеперь должны явно реализовывать интерфейсDrupal\Core\Entity\EntityFormInterface.31 - Введен новый интерфейс
Drupal\Core\Validation\CompositeConstraintInterface, который служит мостом между составными ограничениями Symfony и Drupal.32 - Добавлено новое ограничение
Sequentiallyдля последовательной валидации с показом только первой ошибки.33 - Добавлен опциональный обработчик
link_targetдля типов сущностей, позволяющий единообразно генерировать URL-адреса при создании ссылок.34 - Добавлены stream wrapper'ы
module://иtheme://для удобного обращения к файлам в модулях и темах с автоматической проверкой доступности расширений.35 - Добавлено событие
PreEntityImportEvent, позволяющее изменять данные сущности перед созданием в процессе импорта содержимого.36 - Добавлен новый плагин автодополнения сущностей для полей-ссылок на сущности типа
block_content. Теперь блоки, которые не помечены как «Переиспользуемый», автоматически исключаются из результатов автодополнения.37
Производительность и кеширование
- Теперь автоматически генерируется класс
Drupal\DrupalInstalled. Он содержит хеш всех установленных зависимостей проекта в константеVERSIONS_HASH.38 Это значение будет использоваться для генерации ID кеша контейнера. При изменении кодовой базы и её зависимостей, будет создан новый контейнер. - Работа Page Cache Middleware была оптимизирована благодаря использованию Service Closure.39 Это позволяет ускорить обработку кешированных страниц.
- Метод
EntityStorageBase::loadMultiple()теперь использует Fiber'ы40 для ленивой загрузки нескольких сущностей.41 - Удалён устаревший кеш предзагрузки синонимов путей. Вместо него внедрён новый механизм, который основан на Fiber'ах40 и предназначен для оптимизации загрузки синонимов.42
- Введен новый кеш-контейнер (cache bin) —
cache.memory.43 Он заменяет устаревшийcache.static. - Блоки, которые реализуют интерфейс
Drupal\Core\Cache\CacheOptionalInterface, не будут иметь собственных записей в рендер-кеше. Благодаря этому можно будет кешировать страницы с такими блоками в динамическом кеше.44 - Метод
ContentEntityStorageBase::loadRevision()теперь включает механизмы статического и постоянного кеширования.45 - Логика управления памятью в
MigrateExecutableбыла удалена. Теперь для статического кеширования сущностей применяется LRU-кеш46.47 - Для оптимизации работы с Fiber'ами добавлено перечисление
FiberResumeType. Оно позволяет указать, что приостановленный Fiber может быть немедленно возобновлён.48 - Теперь метод
MemoryBackend::garbageCollection()реализует очистку кеша. Он удаляет из памяти элементы, помеченные как невалидные (например, с истекшим сроком действия).49 Раньше этот метод был пустым. Из-за этого в оперативной памяти постоянно накапливались неиспользуемые данные — невалидные записи не удалялись автоматически. - Работа системы хуков изменена: теперь хуки не выступают в роли слушателей событий.50 Такое изменение помогает улучшить производительность системы.
Устаревшая функциональность и изменения в обратной совместимости
- Метод
FileSystemInterface::basename()признан устаревшим. Теперь рекомендуется использовать нативную PHP-функциюbasename().51 - Хук
hook_ranking()признан устаревшим. В качестве замены введена функцияhook_node_search_ranking().52 - Классы, трейты, плагины и компоненты, связанные с миграцией данных из старых версий Drupal, признаны устаревшими. Это сделано в рамках подготовки к полному удалению модуля Drupal Migrate в Drupal 12.5354
- Модуль Migrate Drupal UI, предоставляющий пользовательский интерфейс для миграции данных из предыдущих версий Drupal, объявлен устаревшим.55
- Многочисленные плагины миграции объявлены устаревшими.56 Они предназначены для переноса данных с устаревших версий Drupal.
- Библиотеки
comment/drupal.comment-new-indicatorиcomment/drupal.node-new-comments-linkобъявлены устаревшими и заменены на аналоги из модуля history.57 - Рефакторинг модуля Responsive Image: устаревшие функции заменены методами сервиса
ResponsiveImageBuilderдля улучшения структуры кода и перехода на ООП-подход.58 - Маршрут
comment.new_comments_node_linksи метод контроллераDrupal\comment\Controller\CommentController::renderNewCommentsNodeLinksобъявлены устаревшими.59 Вместо них рекомендуется использовать новый маршрутhistory.new_comments_node_linksи методDrupal\history\Controller\HistoryController::renderNewCommentsNodeLinks. - Метод
WorkspaceManagerInterface::purgeDeletedWorkspacesBatch()объявлен устаревшим без замены.60 - Функция
_system_default_theme_features()объявлена устаревшей.61 - Метод
theme_get_setting()объявлен устаревшим; вместо него нужно использовать сервисDrupal\Core\Extension\ThemeSettingsProvider.62 - Методы
ModuleHandler::addProfile()иModuleHandler::addModule()больше не выполняют никаких действий и запланированы к удалению в Drupal 12.63 - Функция
file_managed_file_submit()объявлена устаревшей и заменена методом\Drupal\file\Element\ManagedFile::submit().64 - Объявлено устаревшим расширение
.engineдля шаблонизаторов. Теперь шаблонизаторы должны реализовываться как сервисы с меткойtheme_engine.65 - Сервис
workspaces.associationобъявлен устаревшим, добавлен новый сервисworkspaces.tracker.66 - Вызов метода
Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()с нестроковым идентификатором типа сущности (например,NULL) объявлен устаревшим.67 - Объявлено устаревшим сопоставление
NULL-значений через пустой ключ в плагинеStaticMapбез указания значения по умолчанию или опции bypass.68 - Объявлен устаревшим модуль Field Layout.69
- Метод
Drupal\comment\CommentManagerInterface::getCountNewComments()объявлен устаревшим. Рекомендуется использоватьDrupal\history\HistoryManager::getCountNewComments().70 Токенcomment-count-newперемещён из модуля comment в модуль history. - Удален кастомный валидатор
AtLeastOneOfConstraintValidatorв пользу стандартной реализации Symfony.71 - Пакет
doctrine/annotationsбыл форкнут в ядро Drupal для поддержки устаревшего функционала аннотаций до версии Drupal 13.72 - Следующие классы объявлены устаревшими:
ArchiverException,ArchiverInterface,ArchiverManager,Tar,Zip.73 КлассArchiveTar(обёртка вокруг PEAR Archive_Tar) остаётся доступным для использования в интерфейсе импорта и экспорта конфигурации. - Обращение к
$this->containerв функциональных тестах теперь объявлено устаревшим.74 - Вызов метода
FieldStorageDefinitionInterface::getPropertyDefinition()с параметром$name, равнымNULL, теперь объявлен устаревшим.75 - Функция
content_translation_field_sync_widgetи файлcontent_translation.admin.incобъявлены устаревшими. Теперь следует использовать сервисFieldSyncWidget.76 - Метод
ImageStyle::getReplacementID()объявлен устаревшим, поскольку он не используется.77 Теперь для получения ID замены нужно обращаться к хранилищу сущностей. - Единый хук
hook_requirements()объявлен устаревшим. Теперь для проверок системных требований на разных этапах работы сайта введены отдельные хуки и интерфейс.78 - Модуль Contact больше не входит в состав Standard79 и Demo Umami80 профилей. В будущем его планируют пометить как устаревший и перевести в разряд контрибных модулей.81
Пользовательский интерфейс и UX
- Поля «Помещено на главную страницу» (Promoted) и «Закреплять вверху списков» (Sticky) по умолчанию скрыты для всех новых типов содержимого.82 При этом сами поля не изменились — вы можете вернуть их в форму редактирования материала через раздел «Управление отображением формы» для нужного типа материала.
- Теперь можно управлять порядком элементов в верхней панели (top bar в navigation).83
- Если ограничение размера файла не задано, его отображение при загрузке удалено.84
- Появился новый фильтр Entity Links и плагин для CKEditor 5. Они позволяют создавать ссылки на сущности через автодополнение.85
- Для Single-Directory Components (SDC) добавлено свойство
noUi. Оно позволяет скрывать компоненты в интерфейсах конструкторов страниц.86
Темы и фронтенд
- Добавлена возможность помечать устаревшие suggestions для тем-хуков с указанием сообщения об их замене или удалении в будущих версиях.87
- Добавлена поддержка рендер-массивов и
MarkupInterfaceвнутри Twig-тега{% trans %}.88 Теперь эти типы данных можно безопасно использовать в переводах — раньше это вызывало ошибки и предупреждения. - Переход на использование
yieldв рендеринге Twig — это подготовка к Twig 4 и поддержка асинхронной обработки.89 - Объявлены устаревшими функции предварительной обработки шаблонов (
template_preprocess_HOOK), а также ключиfileиincludesв определениях hook_theme().90 - Введено новое средство для разрешения доступа Twig-шаблонов к методам объектов — атрибут
#[TwigAllowed].91 - Для панели инструментов Navigation и верхней панели административного интерфейса добавлен CSS-сброс.92 Это поможет предотвратить влияние стилей внешней темы на их оформление.
Содержимое и структура данных
- Улучшено обновление временных меток: при публикации рабочего пространства (модуль Workspace) временная метка изменения автоматически обновляется для всех связанных сущностей.93
Модули и расширяемость
- Валидация для конфигурации
core.extensionулучшена — добавлены ограничения для проверки корректности данных.94 - Теперь хуки-функции относятся к текущему модулю, а не к наиболее специфичному соответствию, как было раньше.95
- Создание плагинов стало проще благодаря внедрению автосвязывания.96 Теперь для указания зависимостей можно использовать аттрибуты вместо явного определения метода
create().97 - Добавлена система Workspace Provider. Это плагин-подобная архитектура, основанная на сервисах с метками, которая позволяет создавать пользовательские типы рабочих пространств (workspaces) с особым поведением.98
- Теперь плагины, которые используются сущностями с коллекцией плагинов, могут реагировать на удаление зависимостей этих сущностей.99
- Модуль Navigation стал стабильным.100
Конфигурация и развертывание
- Добавлена возможность отключить автоматическое создание файлов
.htaccessс помощью параметра$settings['auto_create_htaccess'] = FALSE.101 Это пригодится для сайтов, которые не используют Apache или уже имеют защиту директорий (например,sites/default/files) на уровне сервера. - Добавлена возможность исключать отдельные пакеты из процесса очистки с помощью плагина
drupal/core-vendor-hardeningв Composer.102 - Удалена возможность настраивать пути к Composer и rsync через конфигурацию (
package_manager.settings:executables). Вместо этого вsettings.phpдобавлены новые параметры —package_manager_composer_pathиpackage_manager_rsync_path. Они позволяют явно указывать пути, что повышает безопасность.103 - Теперь при переключении сайта в режим обслуживания или выходе из него в лог по умолчанию (
default) записывается соответствующее сообщение.104 - Экспортёр стандартного содержимого (default content) теперь по умолчанию пропускает поля типа
createdпри генерации экспорта.15
Тестирование и качество кода
- Введено обязательное требование использовать атрибут
#[RunTestsInSeparateProcesses]для всех Kernel, Functional и FunctionalJavascript тестов.105 - Добавлена возможность использования hook-атрибутов для объявления реализаций хуков в Kernel-тестах.106
- Удалена поддержка PHPUnit 10 — теперь для тестирования можно использовать только PHPUnit 11.107
Доступ и разрешения
- Теперь доступно новое разрешение
rebuild node access permissions, которое позволяет перестроить разрешения для типов материалов (node).108 Раньше для этого нужно было иметь разрешениеadminister nodes. - Введено новое разрешение
administer node published status. Оно даёт пользователям определённой роли возможность переключать флажок «Опубликовано» при редактировании материалов.109
Многоязычность и интернационализация
- Атрибут
hreflangв блоке переключения языков заменён наdata-drupal-language.110
Системные требования и совместимость
- Выполнены работы по обеспечению совместимости кодовой базы с PHP 8.5111.
- Рекомендованная версия PHP — 8.4112.113
- Классы подключения к базе данных (БД) для PHP 8.4+ обновлены. Для обновления использовались PDO-драйверы:
Pdo\Mysql,Pdo\Pgsql,Pdo\Sqlite.114 Благодаря этому устранены предупреждения об устаревании в PHP 8.5 и обеспечена совместимость - Несовместимость Single Directory Components (SDC) с Windows устранена. Для этого в именах файлов кеша Twig двоеточия заменили на подчёркивания.115
Drupal 11.3.0. Релизы Drupal. На момент публикации материала страница ещё не существует. ↩
Revisions for Experimental Symfony Mailer Module История изменений Drupal Core. Дата обращения: 2025-12-17. ↩
Hooks in themes can now be OOP. История изменений Drupal Core. 2025-10-27. Дата обращения: 2025-11-10. ↩
New Object oriented approach for working with form/render arrays. История изменений Drupal Core. 2025-06-27. ↩ ↩
Widget elements can be written using object oriented approach. История изменений Drupal Core. 2025-06-27. ↩
</> htmx high power tools for HTML. Официальный сайт. ↩
Little HTMX Book. Краткое руководство по HTMX. 2025-09-11. ↩
Ajax subsystem now includes HTMX. История изменений Drupal Core. 2025-09-11. ↩ ↩ ↩ ↩ ↩
Route option added for routes designed to serve HTMX requests. История изменений Drupal Core. 2025-10-31. Дата обращения: 2025-11-12. ↩
New trait assists classes building render arrays for HTMX.. История изменений Drupal Core. 2025-10-14. ↩
ConfigSingleExportForm now has a dynamically updated URL. История изменений Drupal Core. 2025-10-28. Дата обращения: 2025-11-12. ↩
content:export command added to help with recipe development. История изменений Drupal Core. 2025-07-15. ↩
Support exporting content and its dependencies to a folder structure on disk. Задача на Drupal.org. ↩
Default Content. Проект на Drupal.org. ↩
"Created" fields are excluded from default content by default. История изменений Drupal Core. 2025-11-14. Дата обращения: 2025-11-18. ↩ ↩
Улучшенный модуль MySQL (MySQL Improved). Руководство по PHP. ↩
A new database driver (mysqli) for MySQL/MariaDB for parallel queries. История изменений Drupal Core. 2025-06-19. ↩
Revolt - The rock-solid event loop for PHP. Официальный сайт и документация. ↩
Add mergeWith() to AjaxResponse for merging with another response. История изменений Drupal Core. 2025-06-20. ↩
Added support for @> as a shorthand for !service_closure in services.yml files. История изменений Drupal Core. 2025-08-06. ↩
EntityController::addPage now requires the $request parameter. История изменений Drupal Core. 2025-10-11. ↩
Add AJAX command to Views module that sets the browser URL without refreshing the page.. История изменений Drupal Core. 2025-10-16. ↩
New WorkspaceSwitchEvent event added. История изменений Drupal Core. 2025-10-23. Дата обращения: 2025-11-10. ↩
Drupal\taxonomy\Form\OverviewTerms now extends Drupal\Core\Entity\EntityForm. История изменений Drupal Core. 2025-10-24. Дата обращения: 2025-11-10. ↩
node.add and node.add_page routes have new aliases. История изменений Drupal Core. 2025-10-31. Дата обращения: 2025-11-13. ↩
New ConfigImporterFactory service. История изменений Drupal Core. 2025-11-01. Дата обращения: 2025-11-13. ↩
Method getValuesSetDuringRequest() added to Drupal\Core\State\StateInterface. История изменений Drupal Core. 2025-11-01. Дата обращения: 2025-11-13. ↩
Convert batch callbacks to CallableResolver. Задача на Drupal.org. Дата обращения: 2025-11-17. ↩
Form API callbacks now support callables supported by the CallableResolver. История изменений Drupal Core. 2025-12-03. Дата обращения: 2025-12-04. ↩
Add a recursive sort-by-key function to SortArray. Задача на Drupal.org. Дата обращения: 2025-11-18. ↩
Classes used in entity form handlers must implement Drupal\Core\Entity\EntityFormInterface. История изменений Drupal Core. 2025-11-14. Дата обращения: 2025-11-18. ↩
\Drupal\Core\Validation\CompositeConstraintInterface added to bridge Symfony's Composite constraints to Drupal. История изменений Drupal Core. 2025-11-18. Дата обращения: 2025-11-20. ↩
New Sequentially constraint added to core. История изменений Drupal Core. 2025-11-18. Дата обращения: 2025-11-20. ↩
Entity Type definitions can now optionally provide a "link_target" handler. История изменений Drupal Core. 2025-11-20. Дата обращения: 2025-11-20. ↩
module:// and theme:// stream wrappers added to core. История изменений Drupal Core. 2025-11-22. Дата обращения: 2025-11-24. ↩
Dispatch an event for manipulating entity data during content import. Задача на Drupal.org. Дата обращения: 2025-11-24. ↩
Block content entity reference fields now use the BlockContentSelection plugin by default. История изменений Drupal Core. 2025-11-28. Дата обращения: 2025-12-02. ↩
Drupal Scaffold composer plugin generates a new \Drupal\DrupalInstalled class. История изменений Drupal Core. 2025-09-09. ↩
Page Cache Middleware uses Service Closure to speed up serving cached pages. История изменений Drupal Core. 2025-09-25. ↩
Lazy load multiple entities at a time using fibers. Задача на Drupal.org. 2025-09-29. ↩
The path alias preload cache has been removed. История изменений Drupal Core. 2025-09-30. ↩
New cache.memory cache bin, replaces cache.static, MemoryCacheInterface alias deprecated. История изменений Drupal Core. 2025-10-22. Дата обращения: 2025-11-06. ↩
Block plugins implementing CacheOptionalInterface will not have their own render cache entries. История изменений Drupal Core. 2025-10-26. Дата обращения: 2025-11-10. ↩
Loading revisions now uses the static and persistent cache like. История изменений Drupal Core. 2025-11-12. Дата обращения: 2025-11-13. ↩
Least Recently Used — «наименее недавно использованный». Его особенность в том, что он сам удаляет данные, которые не использовались дольше всего, когда кеш достигает лимита по объёму. Это позволяет автоматически поддерживать оптимальный объём занимаемой памяти без ручного вмешательства.
В Drupal 11.2 была добавлена реализация
Drupal\Core\Cache\MemoryCache\LruMemoryCache, которая используется для сервисаentity.memory_cache. По умолчанию лимит составляет 1 000 элементов. При необходимости его можно изменить в файлеservices.ymlпроекта через параметрentity.memory_cache.slots. ↩Memory management removed from MigrateExecutable. История изменений Drupal Core. 2025-11-10. Дата обращения: 2025-11-17. ↩
FiberResumeType enum introduced to allow fiber suspensions to indicate the intent. История изменений Drupal Core. 2025-11-28. Дата обращения: 2025-12-02. ↩
MemoryBackend::garbageCollection() now removes invalid items from memory. История изменений Drupal Core. 2025-11-27. Дата обращения: 2025-12-02. ↩
Hooks are no longer event listeners. История изменений Drupal Core. 2025-10-11. ↩
FileSystemInterface::basename() deprecated, use PHP native basename() instead. История изменений Drupal Core. 2025-09-19. ↩
hook_ranking() has been renamed to hook_node_search_ranking(). История изменений Drupal Core. 2025-09-19. ↩
Migrate source plugins for legacy upgrade are deprecated. История изменений Drupal Core. 2025-09-25. ↩
Migrate field plugins are deprecated. История изменений Drupal Core. 2025-10-08. ↩
Migrate Drupal UI is deprecated. История изменений Drupal Core. 2025-11-14. Дата обращения: 2025-11-18. ↩
Migrate process plugins for legacy upgrade are deprecated. История изменений Drupal Core. 2025-11-28. Дата обращения: 2025-12-02. ↩
The comment/drupal.comment-new-indicator and comment/drupal.node-new-comments-link libraries have been deprecated. История изменений Drupal Core. 2025-09-25. ↩
_responsive_image_build_source_attributes(), responsive_image_get_image_dimensions(), responsive_image_get_mime_type(), _responsive_image_image_style_url() replaced with ResponsiveImageBuilder. История изменений Drupal Core. 2025-09-25. ↩
The comment.new_comments_node_links route and CommentController::renderNewCommentsNodeLinks are deprecated. История изменений Drupal Core. 2025-10-13. ↩
WorkspaceManagerInterface::purgeDeletedWorkspacesBatch() has been deprecated. История изменений Drupal Core. 2025-10-23. Дата обращения: 2025-11-10. ↩
_system_default_theme_features is deprecated. История изменений Drupal Core. 2025-10-24. Дата обращения: 2025-11-10. ↩
theme_get_setting() is deprecated. История изменений Drupal Core. 2025-10-24. Дата обращения: 2025-11-10. ↩
ModuleHandler addProfile and addModule no longer do anything.. История изменений Drupal Core. 2025-10-29. Дата обращения: 2025-11-12. ↩
file_managed_file_submit() is deprecated. История изменений Drupal Core. 2025-10-30. Дата обращения: 2025-11-12. ↩
The .engine extension has been deprecated. Use tagged services instead.. История изменений Drupal Core. 2025-11-04. Дата обращения: 2025-11-13. ↩
The workspaces.association service has been replaced by workspaces.tracker. История изменений Drupal Core. 2025-11-10. Дата обращения: 2025-11-17. ↩
Do not call \Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo() with a NULL value. История изменений Drupal Core. 2025-11-14. Дата обращения: 2025-11-18. ↩
\Drupal\migrate\Plugin\migrate\process\StaticMap::transform() cannot map NULL values unless there is a default value or bypass is set. История изменений Drupal Core. 2025-11-14. Дата обращения: 2025-11-18. ↩
Field Layout module is deprecated. История изменений Drupal Core. 2025-11-14. Дата обращения: 2025-11-18. ↩
Deprecate CommentManagerInterface::getCountNewComments. Задача на Drupal.org. Дата обращения: 2025-11-20. ↩
AtLeastOneOfConstraintValidator has been replaces by the default Symfony implementation. История изменений Drupal Core. 2025-11-18. Дата обращения: 2025-11-20. ↩
doctrine/annotations has been forked into core. История изменений Drupal Core. 2025-11-22. Дата обращения: 2025-11-24. ↩
ArchiverManager and other archive management code is deprecated. История изменений Drupal Core. 2025-11-17. Дата обращения: 2025-11-18. ↩
Accessing $this->container from functional tests is deprecated. История изменений Drupal Core. 2025-11-20. Дата обращения: 2025-12-02. ↩
Calls to \Drupal\Core\Field\FieldStorageDefinitionInterface::getPropertyDefinition() will trigger a deprecation if $name is not a string. История изменений Drupal Core. 2025-11-17. Дата обращения: 2025-12-02. ↩
content_translation_field_sync_widget has been deprecated. История изменений Drupal Core. 2025-11-27. Дата обращения: 2025-12-02. ↩
ImageStyle::getReplacementID is deprecated. История изменений Drupal Core. 2025-11-28. Дата обращения: 2025-12-02. ↩
hook_requirements deprecated in favor of separate runtime and update hooks and install-time requirements checks. История изменений Drupal Core. 2025-12-03. Дата обращения: 2025-12-04. ↩
Remove Contact module from the Standard profile. Задача на Drupal.org. Дата обращения: 2025-10-20. ↩
Remove Contact module from the Umami profile. Задача на Drupal.org. Дата обращения: 2025-10-20. ↩
[meta] Tasks to deprecate the Contact module. Задача на Drupal.org. Дата обращения: 2025-10-20. ↩
Be able to control the order of top bar items. Задача на Drupal.org. Дата обращения: 2025-11-05. ↩
Don't display size limit when there is no limit configured for file upload. Задача на Drupal.org. Дата обращения: 2025-11-05. ↩
A new Entity Links Filter format and CKEditor 5 plugin has been added. История изменений Drupal Core. 2025-11-20. Дата обращения: 2025-11-20. ↩
New noUi property allowing page builders to exclude SDCs. История изменений Drupal Core. 2025-11-22. Дата обращения: 2025-11-24. ↩
Theme suggestions can now be deprecated. История изменений Drupal Core. 2025-08-05. ↩
{% trans %} Twig tag can contain rendered expressions that return render arrays and MarkupInterface objects. История изменений Drupal Core. 2025-09-16. ↩
Twig rendering now uses yield. История изменений Drupal Core. 2025-09-22. ↩
template_preprocess_HOOK, and the file and includes keys in hook_theme definitions have been deprecated.. История изменений Drupal Core. 2025-10-16. ↩
New TwigAllowed method attribute. История изменений Drupal Core. 2025-11-17. Дата обращения: 2025-11-20. ↩
Publishing a workspace will update the changed time for its entities. История изменений Drupal Core. 2025-07-19. ↩
Add validation constraints to core.extension. Задача на Drupal.org. Дата обращения: 2025-10-20. ↩
Legacy hook functions are now attributed to the current module instead of the most specific match. История изменений Drupal Core. 2025-10-08. ↩
PluginBase provides create() factory method with autowired parameters. История изменений Drupal Core. 2025-10-14. ↩
Реализация, как и в случае с контроллерами, осуществляется через трейт
AutowiredInstanceTraitи рефлексию в рантайме. По сути, это автосвязывание для бедных — иначе не назвать. При этом автосвязывание будет работать только с сервисами. Другие варианты автосвязывания, включая простой#[Autowire(param: 'app.root')], уже не поддерживаются.💡 Pro Tip: вместо того чтобы использовать
AutowireTraitдля контроллеров и форм, просто регистрируйте их как сервис. Так вы получите больше возможностей, сократите объём кода и повысите скорость работы. ↩New Workspace Provider system. История изменений Drupal Core. 2025-10-21. Дата обращения: 2025-11-05. ↩
Plugins used in entities with plugin collections can react when the entities' dependencies are removed. История изменений Drupal Core. 2025-10-22. Дата обращения: 2025-11-05. ↩
Automatic creation of .htaccess files can be disabled. История изменений Drupal Core. 2025-06-30. ↩
The vendor hardening plugin can be configured to skip cleaning certain packages. История изменений Drupal Core. 2025-07-16. ↩
Package Manager's path to Composer is no longer configurable. История изменений Drupal Core. 2025-09-09. ↩
Log changes to maintenance mode. Задача на Drupal.org. Дата обращения: 2025-11-13. ↩
#[RunTestsInSeparateProcesses] attribute is required for all Kernel, Functional and FunctionalJavascript tests. История изменений Drupal Core. 2025-10-13. ↩
Kernel tests can use hook attributes to test hooks. История изменений Drupal Core. 2025-10-26. Дата обращения: 2025-11-10. ↩
Removed support for PHPUnit 10. История изменений Drupal Core. 2025-10-28. Дата обращения: 2025-11-10. ↩
Access to rebuild node permissions now requires the "rebuild node access permissions" permission. История изменений Drupal Core. 2025-10-14. ↩
New permission available to control the Published status of Nodes. История изменений Drupal Core. 2025-10-15. ↩
Invalid attributes are changed in language switcher block HTML. История изменений Drupal Core. 2025-11-08. Дата обращения: 2025-11-17. ↩
Recommend PHP 8.4 for Drupal 11.3 and 10.6. Задача на Drupal.org. Дата обращения: 2025-11-17. ↩
Using specific PDO drivers instead of PDOConnection on PHP 8.4+. История изменений Drupal Core. 2025-09-18. ↩
Single Directory Components incompatible with Windows. Задача на Drupal.org. Дата обращения: 2025-12-02. ↩
Комментарии
Спасибо за подробную компиляцию!