Перейти к содержимому

PHP-only block registration: блоки без JavaScript

С версии WordPress 7.0 блок можно зарегистрировать одним PHP-вызовом — без block.json, без сборки JavaScript и без registerBlockType на клиенте. Флаг supports.autoRegister включает режим, в котором ядро само создаёт запись блока в редакторе и само рисует контролы атрибутов в боковой панели. Такой блок всегда рендерится на сервере через render_callback.

Подход рассчитан на серверные блоки: вывод связанных записей, виджет-подобные настройки, блоки темы, которые раньше делали шорткодом. Он не заменяет клиентскую модель блоков и никогда не сравнится с ней по возможностям — интерактивные блоки по-прежнему пишутся на JavaScript.

register_block_type получает имя блока и массив настроек. Обязательны два элемента: render_callback и supports.autoRegister => true.

add_action( 'init', 'myplugin_register_blocks' );
function myplugin_register_blocks() {
register_block_type(
'myplugin/notice',
array(
'title' => __( 'Notice', 'myplugin' ),
'category' => 'design',
'attributes' => array(
'text' => array(
'label' => __( 'Текст', 'myplugin' ),
'type' => 'string',
'default' => __( 'Обратите внимание', 'myplugin' ),
),
'level' => array(
'label' => __( 'Уровень', 'myplugin' ),
'type' => 'string',
'enum' => array( 'info', 'warning', 'error' ),
'default' => 'info',
),
),
'render_callback' => 'myplugin_render_notice',
'supports' => array(
'autoRegister' => true,
'html' => false,
),
)
);
}
function myplugin_render_notice( $attributes ) {
$level = in_array( $attributes['level'], array( 'info', 'warning', 'error' ), true )
? $attributes['level']
: 'info';
return sprintf(
'<div %1$s><p class="notice notice--%2$s">%3$s</p></div>',
get_block_wrapper_attributes(),
esc_attr( $level ),
esc_html( $attributes['text'] )
);
}

Что здесь важно:

  • render_callback обязателен. Без него блок не отрисуется: у PHP-only блока нет сохранённой разметки, только серверный рендер.
  • get_block_wrapper_attributes() отдаёт классы и стили, которые редактор ждёт на обёртке блока. Без него поддержка цветов, отступов, типографики и пользовательские классы не применятся, хотя сами supports формально включены.
  • supports.html: false убирает вкладку «Дополнительно → HTML-элемент», которая для серверного блока бессмысленна.

Ядро строит контрол по типу атрибута:

Тип атрибутаКонтрол в боковой панели
stringтекстовое поле
string + enumвыпадающий список
number, integerчисловое поле
booleanпереключатель

Это весь поддерживаемый набор на 7.0. Медиафайлы, rich text, многострочный текст, дата и цвет как отдельные поля не поддерживаются.

Дополнительные правила:

  • label переводим. Подпись контрола берётся из атрибута label и оборачивается в __() — без него в панели будет техническое имя атрибута.
  • Только несурced-атрибуты. Атрибут должен сохраняться в JSON-комментарии блока, а не извлекаться из HTML (source). Для sourced-атрибутов контрол не создаётся.
  • role: local — без контрола. Атрибуты с ролью local не сохраняются в контенте (это состояние редактора), поэтому панель их не показывает.
  • enum без пар «ключ → подпись». В списке выводятся сами значения (info, warning), локализовать их нельзя. Если нужны человеческие подписи — выносите выбор в отдельный атрибут с enum из чисел и маппинг в PHP-рендере.

Ограничения, о которых стоит знать заранее

Заголовок раздела «Ограничения, о которых стоит знать заранее»

Нет save. Блок не хранит свою разметку в post_content — только атрибуты. Сохранить HTML на этапе редактирования (как у статических блоков) нельзя, и в ядре не планируют это добавлять: пришлось бы решать задачу надёжного извлечения атрибутов из HTML.

Нет внутренних блоков. InnerBlocks в PHP-only блоках недоступен. Контейнер с вложенными колонками или группами так не собрать — для этого нужен обычный клиентский блок. Практический потолок подхода — «серверный виджет»: один блок, несколько настроек, готовый вывод.

Каждое изменение — поход на сервер. Правка атрибута вызывает повторный рендер через REST. Для блока, который тянет тяжёлый запрос, это заметно замедляет редактор: кэшируйте выборку и не запускайте дорогие запросы в render_callback без нужды.

Контекст поста при повторном рендере. uses_context с postId заполняется при первом рендере, но не при последующих вызовах из редактора через REST (тикет #65331, перенесён в Gutenberg #78619). Если блоку нужен ID записи — передавайте его атрибутом или получайте иначе, не полагайтесь на контекст.

Версия важна. Флаг появился в 7.0 (первое рабочее состояние — 7.0-beta3). На более старом ядре неизвестный ключ в supports игнорируется молча, и блок просто не появится в редакторе. Проверяйте версию явно:

if ( version_compare( get_bloginfo( 'version' ), '7.0', '<' ) ) {
return; // на старом ядре блок регистрируем старым способом
}

Публичной функции вида current_theme_supports() для проверки autoRegister нет — остаётся сверка версии.

  • Классические темы. Блок даёт нормальные контролы вместо шорткода: пользователь видит настройки в панели, а не атрибуты в квадратных скобках.
  • Серверные данные. Вывод последних записей, курса валют, списка услуг из настроек — то, что бессмысленно хранить в контенте.
  • Блоки темы под один проект. Авторская подпись, CTA-секция, блок с фиксированной вёрсткой и парой настроек.
  • Миграция шорткодов. Быстрый путь перевести шорткод в блок, не поднимая сборку JS.

Не подходит для интерактивных блоков (табы, слайдеры, аккордеоны с состоянием в редакторе), для контейнеров с вложенностью и для блоков, где важна мгновенная реакция интерфейса без обращения к серверу.

  • Блок виден в инсертере и вставляет нужную разметку на фронте.
  • Контролы появились в боковой панели, подписи переведены.
  • Стили из supports (цвет, отступы, ширина) применяются к обёртке.
  • Повторное открытие записи сохраняет значения атрибутов.
  • Рендер не делает тяжёлых запросов: откройте запись в редакторе и посмотрите время ответа REST-запроса блока.