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+ як React Server Component (RSC), об'єднаний із клієнтськими реактивними сигналами (Signals):
render.php): виконує швидкий індексний запит до БД або WP_Object_Cache, генерує семантичний HTML із системними токенами через get_block_wrapper_attributes() та вбудовує початковий стан (wp_interactivity_state() / wp_interactivity_data_wp_context()).WP_HTML_Tag_Processor): якщо перед відправленням потрібно безпечно додати ARIA-атрибути, класи або loading="lazy" у вкладені теги, вбудований потоковий лексер модифікує HTML за частки мілісекунди без регулярних виразів і без важкого DOMDocument.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', передаючи в локальну область видимості $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 Maps і активує реактивні обробники store('agentic/benchmarks') під час дій користувача.
В інженерному стеку 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):
// Безпечно збагачуємо кореневий вузол та кнопки атрибутами телеметрії без регулярних виразів!
$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';
* },
* },
* });
*/
Вкажіть у 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() та закешуйте запити до БД.
Викличте store('namespace/block-name', { actions, callbacks }) з пакета @wordpress/interactivity для миттєвої реакції на дії користувача.