PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / libraries / framework / View / ViewContext.php
kirki / libraries / framework / View Last commit date
TemplateEngine.php 5 days ago View.php 5 days ago ViewContext.php 5 days ago layout-wrapper.php 5 days ago
ViewContext.php
212 lines
1 <?php
2
3 /**
4 * Active view data context stack for site route templates.
5 *
6 * Registers controller and nested partial view data and exposes it via
7 * view_data(), with caller verification to prevent hijacking.
8 *
9 * @package Framework
10 * @subpackage View
11 * @since 2.1.2
12 */
13 namespace Kirki\Framework\View;
14
15 \defined('ABSPATH') || exit;
16 use Kirki\Framework\Supports\Arr;
17 use function Kirki\Framework\app;
18 class ViewContext
19 {
20 /**
21 * Stack of view contexts for the current request (root + nested partials).
22 *
23 * @var array
24 *
25 * @since 2.1.2
26 */
27 protected $stack = [];
28 /**
29 * Whether the shutdown clear hook was registered.
30 *
31 * @var bool
32 *
33 * @since 2.1.2
34 */
35 protected $shutdown_registered = \false;
36 /**
37 * Prepare and push view data for a site route view.
38 *
39 * @param View $view Controller view return value.
40 * @param string $route_name Named route or empty string.
41 * @param string $resolved_path Absolute template filesystem path.
42 *
43 * @return array Final data stored in the pushed context.
44 *
45 * @since 2.1.2
46 */
47 public function prepare(View $view, string $route_name, string $resolved_path)
48 {
49 $engine = app(TemplateEngine::class);
50 $template = $view->get_template();
51 $data = \array_merge($engine->get_shared(), $view->get_data());
52 $this->push(['template' => $template, 'route_name' => $route_name, 'resolved_path' => $this->normalize_path($resolved_path), 'data' => $data]);
53 return $data;
54 }
55 /**
56 * Push a context frame onto the stack.
57 *
58 * @param array $context Context payload with template, route_name, resolved_path, data.
59 *
60 * @return void
61 *
62 * @since 2.1.2
63 */
64 public function push(array $context)
65 {
66 if (isset($context['resolved_path'])) {
67 $context['resolved_path'] = $this->normalize_path((string) $context['resolved_path']);
68 }
69 $this->stack[] = $context;
70 $this->ensure_shutdown_registered();
71 }
72 /**
73 * Pop the top context frame from the stack.
74 *
75 * @return array|null The removed frame, or null when the stack is empty.
76 *
77 * @since 2.1.2
78 */
79 public function pop()
80 {
81 if ($this->stack === []) {
82 return null;
83 }
84 return \array_pop($this->stack);
85 }
86 /**
87 * Read a value from the innermost authorized context.
88 *
89 * Supports dot notation for nested keys (e.g. `product.name`).
90 *
91 * @param string|null $key Data key, or null for the full array.
92 * @param mixed $default Default when missing or unauthorized.
93 *
94 * @return mixed
95 *
96 * @since 2.1.2
97 */
98 public function get($key = null, $default = null)
99 {
100 $frame = $this->authorized_frame();
101 if ($frame === null) {
102 return $key === null ? [] : $default;
103 }
104 if ($key === null) {
105 return $frame['data'];
106 }
107 return Arr::get($frame['data'], $key, $default);
108 }
109 /**
110 * Clear the entire context stack.
111 *
112 * @return void
113 *
114 * @since 2.1.2
115 */
116 public function clear()
117 {
118 $this->stack = [];
119 }
120 /**
121 * Get the top context frame, or null when the stack is empty.
122 *
123 * @return array|null
124 *
125 * @since 2.1.2
126 */
127 public function get_active()
128 {
129 if ($this->stack === []) {
130 return null;
131 }
132 return $this->stack[\count($this->stack) - 1];
133 }
134 /**
135 * Find the innermost stack frame that authorizes the current caller.
136 *
137 * @return array|null
138 *
139 * @since 2.1.2
140 */
141 protected function authorized_frame()
142 {
143 if ($this->stack === []) {
144 return null;
145 }
146 $trace_files = $this->trace_files();
147 for ($index = \count($this->stack) - 1; $index >= 0; $index--) {
148 $frame = $this->stack[$index];
149 if (empty($frame['resolved_path'])) {
150 continue;
151 }
152 if (isset($trace_files[$frame['resolved_path']])) {
153 return $frame;
154 }
155 }
156 return null;
157 }
158 /**
159 * Collect normalized file paths from the current backtrace.
160 *
161 * @return array<string, bool> Map of normalized path => true.
162 *
163 * @since 2.1.2
164 */
165 protected function trace_files()
166 {
167 $files = [];
168 $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
169 foreach ($trace as $frame) {
170 if (empty($frame['file'])) {
171 continue;
172 }
173 $files[$this->normalize_path($frame['file'])] = \true;
174 }
175 return $files;
176 }
177 /**
178 * Register a shutdown callback to clear the stack once.
179 *
180 * @return void
181 *
182 * @since 2.1.2
183 */
184 protected function ensure_shutdown_registered()
185 {
186 if ($this->shutdown_registered) {
187 return;
188 }
189 $this->shutdown_registered = \true;
190 add_action('shutdown', function () {
191 $this->clear();
192 }, 999);
193 }
194 /**
195 * Normalize a filesystem path for comparison.
196 *
197 * @param string $path Absolute path.
198 *
199 * @return string
200 *
201 * @since 2.1.2
202 */
203 protected function normalize_path(string $path)
204 {
205 $real = \realpath($path);
206 if ($real !== \false) {
207 return $real;
208 }
209 return \str_replace('\\', '/', $path);
210 }
211 }
212