title: 'Динамические блоки в WordPress 7.0+: render.php, Interactivity API и HTML API' description: >- Архитектурное руководство по созданию динамических и интерактивных блоков в WordPress 7.0+ (PHP 8.4+): шаблон render.php с get_block_wrapper_attributes(), реактивные острова на базе @wordpress/interactivity (data-wp-*), потоковая модификация DOM через WP_HTML_Tag_Processor и фрагментное кэширование. module: gutenberg-blocks order: 3 difficulty: advanced estimatedTime: 42 мин topics:
Статические блоки идеальны для неизменяемого текста, но в реальных корпоративных и e-commerce системах большая часть интерфейса зависит от живого состояния сервера: карточки актуальных кейсов, фильтры каталога, виджеты цен, персонализированные блоки авторизованного пользователя или телеметрия инфраструктуры. Кроме того, даже для чисто визуальных компонентов дизайн-системы хранение сгенерированного HTML в wp_posts.post_content создаёт проблему масштабирования: чтобы поменять один CSS-класс в шапке карточки на 5 000 страницах сайта, статический блок заставит вас пересохранять каждый пост или поддерживать бесконечную цепочку deprecated.
Динамические блоки (Server-Rendered Blocks) полностью решают обе проблемы. В базе данных хранится только самозакрывающийся комментарий с чистыми JSON-атрибутами:
<!-- wp:agentic-lab/live-benchmarks {"category":"edge","limit":4} /-->
При каждом запросе страницы (или при прогреве кэша) ядро WordPress 7.0+ десериализует эти атрибуты и передаёт их в изолированный серверный шаблон render.php, автоматически снабжая его тремя переменными: $attributes (провалидированный по block.json массив), $content (отрендеренные дочерние блоки InnerBlocks) и $block (полный экземпляр класса WP_Block, включая контекст предков $block->context).
А в WordPress 6.5–7.0+ динамические блоки получили недостающее звено для фронтенда — нативный Interactivity API (@wordpress/interactivity). Вместо подключения тяжёлого React или разрозненных jQuery-скриптов серверный render.php размечает HTML декларативными директивами (data-wp-interactive, data-wp-context, data-wp-on--click, data-wp-bind--hidden), а крошечный реактивный сигнал-движок (~10 KB gzipped) мгновенно гидрирует интерактивное поведение в браузере.
Архитектура динамического блока в WordPress 7.0+ объединяет серверный пререндеринг на PHP с реактивными клиентскими островами (Signals):
render.php): выполняет индексные SQL-выборки или чтение из WP_Object_Cache, формирует семантическую HTML-разметку с системными классами get_block_wrapper_attributes() и внедряет начальное состояние (wp_interactivity_state() / wp_interactivity_data_wp_context()).WP_HTML_Tag_Processor): потоковый лексер модифицирует атрибуты тегов (ARIA, loading="lazy", fetchpriority) без использования регулярных выражений и без создания тяжелого DOM-дерева в памяти.viewScriptModule + @wordpress/interactivity): гидрирует клиентскую интерактивность (переключение фильтров, модальные окна, пагинация) без повторной загрузки страницы.[БД: wp_posts.post_content]
<!-- wp:agentic-lab/live-benchmarks {"env":"prod"} /-->
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ СЕРВЕРНЫЙ КОНВЕЙЕР (PHP 8.4+ / render.php) │
│ 1. Проверка WP_Object_Cache (Fragment Cache Hit?) │
│ 2. Инициализация стейта: wp_interactivity_state('agentic/benchmarks')│
│ 3. Генерация обёртки: get_block_wrapper_attributes() │
│ 4. Постобработка тегов через WP_HTML_Tag_Processor │
└────────────────────────┬─────────────────────────────────────────────┘
│ Отправка готового SSR HTML + JSON State
▼
┌──────────────────────────────────────────────────────────────────────┐
│ БРАУЗЕР КЛИЕНТА (@wordpress/interactivity ES Module) │
│ • Читает data-wp-interactive="agentic/benchmarks" │
│ • Связывает DOM-узлы с реактивными сигналами (data-wp-text, on-click)│
│ • Мгновенно переключает метрики без перезагрузки страницы (0ms RTT) │
└──────────────────────────────────────────────────────────────────────┘Парсер блоков читает JSON-комментарий и автоматически сливает его со значениями default из схемы attributes файла block.json.
Ядро подключает файл, указанный в поле 'render' из block.json, передавая в локальную область видимости $attributes, $content и $block (WP_Block).
Серверный процессор Interactivity API вычисляет начальные значения директив data-wp-bind и data-wp-class прямо на PHP, исключая Layout Shift (CLS = 0).
Класс WP_HTML_Tag_Processor безопасно модифицирует атрибуты тегов в потоковом режиме без построения тяжёлого DOM-дерева.
Браузер загружает viewScriptModule через Import Map и активирует реактивные обработчики store('agentic/benchmarks') при взаимодействии пользователя.
Укажите в block.json поля 'render': 'file:./render.php', 'viewScriptModule': 'file:./view.js' и включите 'supports': { 'interactivity': true }.
В функции registerBlockType() укажите save: () => null (или save: () => <InnerBlocks.Content />, если у блока есть вложенные дочерние блоки).
Выведите корневой узел с get_block_wrapper_attributes(), передайте локальный стейт через wp_interactivity_data_wp_context() и закэшируйте тяжёлые SQL-выборки.
Вызовите store('namespace/block-name', { actions, callbacks }) из пакета @wordpress/interactivity для мгновенной реакции на действия пользователя.
Какое фундаментальное различие существует между wp_interactivity_state() и wp_interactivity_data_wp_context() в серверном шаблоне render.php?
В инженерном стеке Fullstack Agentic Lab динамические блоки на базе render.php и Interactivity API используются для:
LCP < 0.8s), а пользователь переключает окружения (Edge CDN / Origin PHP 8.4 / WASM) в один клик через data-wp-on--click без перезагрузки страницы.render.php в Git-репозитории за 1 секунду обновляет отображение блока на всех 10 000 страницах портала без единой ошибки Block Validation Error.WP_HTML_Tag_Processor: автоматическое внедрение атрибутов доступности (aria-expanded, role), защита внешних ссылок (rel="noopener noreferrer") и привязка интерактивных директив ко вложенным блокам.Сравним классический статический блок, устаревший динамический блок на render_callback и современный интерактивный динамический блок WordPress 7.0+:
Ниже представлен эталонный производственный артефакт для WordPress 7.0+ (PHP 8.4+ и TypeScript 5.8), демонстрирующий полный цикл работы современного интерактивного динамического блока: серверный шаблон render.php с фрагментным кэшированием, инициализацией wp_interactivity_state(), обработкой через WP_HTML_Tag_Processor и клиентским модулем view.ts на базе @wordpress/interactivity.
<?php
/**
* ЧАСТЬ 1: Серверный шаблон build/blocks/live-telemetry/render.php (WordPress 7.0+ / PHP 8.4+)
*
* Доступные переменные в области видимости шаблона:
* @var array<string, mixed> $attributes Валидированные атрибуты блока из block.json.
* @var string $content Отрендеренный HTML внутренних блоков (InnerBlocks).
* @var WP_Block $block Экземпляр текущего блока с доступом к $block->context.
*/
declare(strict_types=1);
defined('ABSPATH') || exit;
$clusterRegion = sanitize_key((string) ($attributes['defaultRegion'] ?? 'eu-central-1'));
$showDetails = (bool) ($attributes['showDetails'] ?? true);
// 1. Получаем данные из быстрого Object Cache (или вычисляем и кэшируем на 5 минут)
$cacheKey = "agentic_telemetry_{$clusterRegion}";
$telemetry = wp_cache_get($cacheKey, 'agentic_blocks');
if (!is_array($telemetry)) {
$telemetry = [
'eu-central-1' => ['latencyMs' => 12, 'rps' => 6400, 'cacheHitRatio' => '99.4%'],
'us-east-1' => ['latencyMs' => 19, 'rps' => 8120, 'cacheHitRatio' => '98.9%'],
'ap-northeast' => ['latencyMs' => 27, 'rps' => 4300, 'cacheHitRatio' => '99.1%'],
];
wp_cache_set($cacheKey, $telemetry, 'agentic_blocks', 300);
}
// 2. Регистрируем глобальное состояние хранилища Interactivity API для SSR-гидратации
wp_interactivity_state('agentic-lab/live-telemetry', [
'regions' => $telemetry,
]);
// 3. Формируем изолированный локальный контекст конкретного экземпляра блока на странице
$blockContext = [
'activeRegion' => $clusterRegion,
'isExpanded' => $showDetails,
];
// 4. Генерируем системную строку атрибутов обёртки (цвета, отступы, классы из supports)
$wrapperAttributes = get_block_wrapper_attributes([
'class' => 'agentic-telemetry-widget',
'data-wp-interactive' => 'agentic-lab/live-telemetry',
]);
// Собираем начальный HTML-каркас
ob_start();
?>
<div <?php echo $wrapperAttributes; ?> <?php echo wp_interactivity_data_wp_context($blockContext); ?>>
<div class="agentic-telemetry-widget__controls" role="tablist" aria-label="Выбор дата-центра">
<?php foreach (array_keys($telemetry) as $regionCode) : ?>
<button
type="button"
class="agentic-region-btn"
data-region="<?php echo esc_attr($regionCode); ?>"
data-wp-on--click="actions.selectRegion"
data-wp-class--is-active="callbacks.isRegionActive"
>
<?php echo esc_html(strtoupper($regionCode)); ?>
</button>
<?php endforeach; ?>
</div>
<div class="agentic-telemetry-widget__metrics" data-wp-bind--hidden="!context.isExpanded">
<div class="metric-box">
<span class="metric-label">P95 Latency:</span>
<strong class="metric-value" data-wp-text="callbacks.currentLatency">
<?php echo esc_html((string) $telemetry[$clusterRegion]['latencyMs'] . ' ms'); ?>
</strong>
</div>
<div class="metric-box">
<span class="metric-label">Throughput:</span>
<strong class="metric-value" data-wp-text="callbacks.currentRps">
<?php echo esc_html((string) $telemetry[$clusterRegion]['rps'] . ' req/s'); ?>
</strong>
</div>
<div class="metric-box">
<span class="metric-label">Edge Hit Ratio:</span>
<strong class="metric-value" data-wp-text="callbacks.currentHitRatio">
<?php echo esc_html((string) $telemetry[$clusterRegion]['cacheHitRatio']); ?>
</strong>
</div>
</div>
</div>
<?php
$rawHtml = (string) ob_get_clean();
// 5. Демонстрация WordPress HTML API (WP_HTML_Tag_Processor):
// Безопасно обогащаем корневой узел и кнопки атрибутами телеметрии без единого Regex!
$processor = new WP_HTML_Tag_Processor($rawHtml);
if ($processor->next_tag(['class_name' => 'agentic-telemetry-widget'])) {
$processor->set_attribute('data-rendered-at', gmdate('c'));
$processor->set_attribute('data-post-id', (string) ($block->context['postId'] ?? 0));
}
echo $processor->get_updated_html();
/*
* ============================================================================
* ЧАСТЬ 2: Клиентский ES-модуль src/blocks/live-telemetry/view.ts (TypeScript 5.8)
* Подключается в block.json через: "viewScriptModule": "file:./view.js"
* ============================================================================
*
* import { store, getContext, getElement } from '@wordpress/interactivity';
*
* interface TelemetryContext {
* activeRegion: string;
* isExpanded: boolean;
* }
*
* interface RegionMetric {
* latencyMs: number;
* rps: number;
* cacheHitRatio: string;
* }
*
* const { state } = store('agentic-lab/live-telemetry', {
* actions: {
* selectRegion() {
* const ctx = getContext<TelemetryContext>();
* const { attributes } = getElement();
* const targetRegion = attributes['data-region'];
* if (typeof targetRegion === 'string') {
* ctx.activeRegion = targetRegion;
* }
* },
* },
* callbacks: {
* isRegionActive(): boolean {
* const ctx = getContext<TelemetryContext>();
* const { attributes } = getElement();
* return ctx.activeRegion === attributes['data-region'];
* },
* currentLatency(): string {
* const ctx = getContext<TelemetryContext>();
* const metrics = state.regions[ctx.activeRegion] as RegionMetric | undefined;
* return metrics ? `${metrics.latencyMs} ms` : 'N/A';
* },
* currentRps(): string {
* const ctx = getContext<TelemetryContext>();
* const metrics = state.regions[ctx.activeRegion] as RegionMetric | undefined;
* return metrics ? `${metrics.rps} req/s` : 'N/A';
* },
* currentHitRatio(): string {
* const ctx = getContext<TelemetryContext>();
* const metrics = state.regions[ctx.activeRegion] as RegionMetric | undefined;
* return metrics ? metrics.cacheHitRatio : 'N/A';
* },
* },
* });
*/
Почему при модификации серверного вывода блоков в фильтре render_block категорически запрещено использовать регулярные выражения preg_replace() вместо WP_HTML_Tag_Processor?