self::labelForPart($block), 'slug' => $block['attrs']['slug'] ?? '', 'seq' => 0, 'skip_depth' => 0, ]; $block['attrs']['__extendify_scope_open'] = 1; return $block; } if (empty(self::$frames)) { return $block; } $frame = self::currentFrame(); if (!$frame) { return $block; } // render_block_data runs top-down (before a block's own render), so an // ignored block is counted here as a leaf and the skip is raised *after* // — its descendants then render with skip_depth > 0 and are not counted. if (($frame['skip_depth'] ?? 0) === 0) { $frame['seq']++; self::$blockStack[] = [ 'name' => $name, 'id' => $frame['seq'], 'label' => $frame['label'], 'slug' => $frame['slug'] ?? '', ]; } if (in_array($name, self::$ignored, true)) { $frame['skip_depth'] = ($frame['skip_depth'] ?? 0) + 1; } self::setCurrentFrame($frame); return $block; } public static function onRenderBlock(string $html, array $block): string { $name = $block['blockName'] ?? ''; if ($name === '') { return $html; } if (self::isTemplatePart($block) && !empty($block['attrs']['__extendify_scope_open'])) { array_pop(self::$frames); return $html; } if (empty(self::$frames)) { return $html; } $frame = self::currentFrame(); $skip = $frame['skip_depth'] ?? 0; // render_block runs bottom-up, so the ignored block's own filter fires // after its descendants — drop one skip level here. Only the outermost // ignored block (skip === 1) was counted in render_block_data, so only it // falls through to be stamped; nested ones and descendants bail. if (in_array($name, self::$ignored, true)) { $frame['skip_depth'] = max(0, $skip - 1); self::setCurrentFrame($frame); if ($skip > 1) { return $html; } } elseif ($skip > 0) { return $html; } // Name-keyed lookup, not array_pop: core/navigation fires render_block // for inner items without firing render_block_data first, so a // straight pop would consume entries belonging to unrelated outer // blocks. If nothing matches, mint a fresh id (nav-link/-submenu path). $info = null; $infoIndex = -1; for ($i = count(self::$blockStack) - 1; $i >= 0; $i--) { if (self::$blockStack[$i]['name'] === $name) { $info = self::$blockStack[$i]; $infoIndex = $i; break; } } if ($info !== null) { array_splice(self::$blockStack, $infoIndex, 1); } else { $frame = self::currentFrame(); if ($frame) { $frame['seq']++; self::setCurrentFrame($frame); $info = [ 'name' => $name, 'id' => $frame['seq'], 'label' => $frame['label'], 'slug' => $frame['slug'] ?? '', ]; } } if ($info && $html) { $tp = new \WP_HTML_Tag_Processor($html); if ($tp->next_tag()) { $tp->set_attribute('data-extendify-part-block-id', (string) (int) $info['id']); $tp->set_attribute('data-extendify-part', $info['label']); if (!empty($info['slug'])) { $tp->set_attribute('data-extendify-part-slug', $info['slug']); } $html = $tp->get_updated_html(); } } return $html; } }