PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / app / Agent / TemplatePartBlockFinder.php

TemplatePartBlockFinder.php in Extendify 3.1.5, at app/Agent/TemplatePartBlockFinder.php

116 lines 4.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Extendify\Agent;
4
5 defined('ABSPATH') || die('No direct access.');
6
7 // Resolves a block inside a template-part by the same preorder numbering
8 // TagTemplateParts assigns at render time, so a client blockId (read off a
9 // data-extendify-part-block-id attribute) maps back to the parsed block.
10 // Shared by SaveController (write) and WPController::getBlockCode (read) so the
11 // two halves can never resolve a different block for the same id.
12 class TemplatePartBlockFinder
13 {
14 // Preorder walk matching TagTemplateParts numbering: nested
15 // core/template-part blocks open a separate seq space (skipped), and the
16 // shared $ignored dynamic blocks (mini-cart, loops, …) count as one leaf
17 // each but are not descended into — the tagger skips their rendered subtree.
18 //
19 // Ref-based navigation blocks (`core/navigation` with a `ref` attr) resolve
20 // their items at render time from a separate wp_navigation post.
21 // TagTemplateParts fires for those rendered items inside the outer
22 // template-part's frame, so each one increments the outer seq. parse_blocks
23 // of the post we're walking only sees the navigation block (empty
24 // innerBlocks for refs), so a naïve walk would under-count. Resolve the nav
25 // ref here and add its block count to the running counter so blockIds
26 // *after* the navigation (e.g. social-link in a sibling social-links block)
27 // still line up.
28 public static function find(
29 array $blocks,
30 int $targetId,
31 int &$maxCounter = 0,
32 array &$visited = []
33 ) {
34 $counter = 0;
35 $found = null;
36
37 $walk = function (array &$list, array $pathSoFar)
38 use (&$walk, &$counter, &$found, $targetId, &$visited) {
39 foreach ($list as $i => &$block) {
40 if ($found !== null) {
41 return;
42 }
43 $name = $block['blockName'] ?? '';
44 if ($name === '') {
45 if (!empty($block['innerBlocks'])) {
46 $walk($block['innerBlocks'], array_merge($pathSoFar, [$i, 'innerBlocks']));
47 }
48 continue;
49 }
50 if ($name === 'core/template-part') {
51 continue;
52 }
53 $counter++;
54 $visited[] = ['c' => $counter, 'n' => $name];
55 if ($counter === $targetId) {
56 $found = ['block' => $block, 'path' => array_merge($pathSoFar, [$i])];
57 return;
58 }
59 // Dynamic self-rendering blocks (loops, woocommerce/mini-cart, …)
60 // count as one leaf but are not descended into: TagTemplateParts
61 // skips their render-injected subtree, so descending here would
62 // drift every later id. Shares the one $ignored list.
63 if (in_array($name, TagTemplateParts::$ignored, true)) {
64 continue;
65 }
66 if ($name === 'core/navigation' && !empty($block['attrs']['ref'])) {
67 $navPost = get_post((int) $block['attrs']['ref']);
68 if ($navPost) {
69 $navBlocks = parse_blocks($navPost->post_content);
70 $before = $counter;
71 self::countBlocksPreorder($navBlocks, $counter);
72 for ($j = $before + 1; $j <= $counter; $j++) {
73 $visited[] = ['c' => $j, 'n' => '(nav-ref-item)'];
74 }
75 }
76 // Ref navs have no innerBlocks in the parsed tree — items
77 // live in the wp_navigation post and can't be replaced from
78 // here anyway (WPNavigationController owns that).
79 continue;
80 }
81 if (!empty($block['innerBlocks'])) {
82 $walk($block['innerBlocks'], array_merge($pathSoFar, [$i, 'innerBlocks']));
83 }
84 }
85 unset($block);
86 };
87 $walk($blocks, []);
88 $maxCounter = $counter;
89
90 return $found;
91 }
92
93 private static function countBlocksPreorder(array $blocks, int &$counter)
94 {
95 foreach ($blocks as $block) {
96 $name = $block['blockName'] ?? '';
97 if ($name === '') {
98 if (!empty($block['innerBlocks'])) {
99 self::countBlocksPreorder($block['innerBlocks'], $counter);
100 }
101 continue;
102 }
103 if ($name === 'core/template-part') {
104 continue;
105 }
106 $counter++;
107 if (in_array($name, TagTemplateParts::$ignored, true)) {
108 continue;
109 }
110 if (!empty($block['innerBlocks'])) {
111 self::countBlocksPreorder($block['innerBlocks'], $counter);
112 }
113 }
114 }
115 }
116