PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.1 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 / TemplateEngine.php
kirki / libraries / framework / View Last commit date
TemplateEngine.php 1 week ago View.php 1 week ago ViewContext.php 1 week ago layout-wrapper.php 1 week ago
TemplateEngine.php
292 lines
1 <?php
2
3 /**
4 * Template engine for rendering site route views with theme layout support.
5 * Resolves views from the theme first, then the application view path.
6 *
7 * @package Framework
8 * @subpackage View
9 * @since 1.0.0
10 */
11 namespace Kirki\Framework\View;
12
13 \defined('ABSPATH') || exit;
14 use RuntimeException;
15 use function Kirki\Framework\app;
16 class TemplateEngine
17 {
18 /**
19 * Shared data available to all views.
20 *
21 * @var array
22 *
23 * @since 1.0.0
24 */
25 protected $shared = [];
26 /**
27 * Cached block theme header markup.
28 *
29 * @var string
30 *
31 * @since 1.0.0
32 */
33 protected $block_header = '';
34 /**
35 * Cached block theme footer markup.
36 *
37 * @var string
38 *
39 * @since 1.0.0
40 */
41 protected $block_footer = '';
42 /**
43 * Share a value with all views.
44 *
45 * @param string $key The data key.
46 * @param mixed $value The data value.
47 *
48 * @return void
49 *
50 * @since 1.0.0
51 */
52 public function share(string $key, $value)
53 {
54 $this->shared[$key] = $value;
55 }
56 /**
57 * Get data shared with all views.
58 *
59 * @return array
60 *
61 * @since 2.1.2
62 */
63 public function get_shared()
64 {
65 return $this->shared;
66 }
67 /**
68 * Render a view template to a string.
69 *
70 * @param string $view The view name in dot notation.
71 * @param array $data The data to pass to the view.
72 * @param bool $layout Whether to wrap with theme header/footer.
73 *
74 * @return string
75 *
76 * @since 1.0.0
77 */
78 public function render(string $view, array $data = [], bool $layout = \true)
79 {
80 $path = $this->resolve_path($view);
81 if ($path === '') {
82 throw new RuntimeException(\sprintf('View [%s] not found.', $view));
83 }
84 $merged = \array_merge($this->shared, $data);
85 $context = app(ViewContext::class);
86 $context->push(['template' => $view, 'route_name' => '', 'resolved_path' => $path, 'data' => $merged]);
87 try {
88 $content = $this->render_file($path);
89 if (!$layout) {
90 return $content;
91 }
92 return $this->wrap_layout($content);
93 } finally {
94 $context->pop();
95 }
96 }
97 /**
98 * Resolve a view name to an absolute file path.
99 *
100 * @param string $view The view name in dot notation.
101 *
102 * @return string Absolute path or empty string when not found.
103 *
104 * @since 1.0.0
105 */
106 public function resolve_path(string $view)
107 {
108 $relative = $this->normalize_template_name($view) . '.php';
109 if (\function_exists('locate_template')) {
110 $theme_path = locate_template('views/' . $relative);
111 if ($theme_path !== '') {
112 return apply_filters('framework/view/path', $theme_path, $view);
113 }
114 }
115 $base_path = app()->view_path();
116 $candidate = \rtrim($base_path, '/\\') . '/' . $relative;
117 $resolved = \realpath($candidate);
118 $base_real = \realpath($base_path);
119 if ($resolved === \false || $base_real === \false || \strpos($resolved, $base_real) !== 0) {
120 return apply_filters('framework/view/path', '', $view);
121 }
122 if (!\is_file($resolved) || !\is_readable($resolved)) {
123 return apply_filters('framework/view/path', '', $view);
124 }
125 return apply_filters('framework/view/path', $resolved, $view);
126 }
127 /**
128 * Normalize a template name into a relative path without extension.
129 *
130 * @param string $template Template name or relative path.
131 *
132 * @return string
133 *
134 * @since 1.0.0
135 */
136 public function normalize_template_name(string $template)
137 {
138 $template = \preg_replace('/\\.php$/i', '', \trim($template));
139 return \str_replace('.', '/', $template);
140 }
141 /**
142 * Check if the current theme is a block theme.
143 *
144 * @return bool
145 *
146 * @since 1.0.0
147 */
148 public function is_block_theme()
149 {
150 return \function_exists('wp_is_block_theme') && wp_is_block_theme();
151 }
152 /**
153 * Render a PHP template file without extracting data into local variables.
154 *
155 * Templates must read data via view_data().
156 *
157 * @param string $path Absolute template path.
158 *
159 * @return string
160 *
161 * @since 1.0.0
162 */
163 protected function render_file(string $path)
164 {
165 \ob_start();
166 // phpcs:ignore Framework.NamingConventions.SnakeCaseVariable.NotSnakeCase
167 (static function ($__path) {
168 // phpcs:ignore Framework.NamingConventions.SnakeCaseVariable.NotSnakeCase
169 require $__path;
170 })($path);
171 return (string) \ob_get_clean();
172 }
173 /**
174 * Wrap rendered content with theme header and footer.
175 *
176 * Assembles HTML as a string. Dynamic view data must be escaped in templates
177 * before it becomes part of $content.
178 *
179 * @param string $content The view content.
180 *
181 * @return string
182 *
183 * @since 1.0.0
184 */
185 public function wrap_layout(string $content)
186 {
187 return $this->render_header() . $content . $this->render_footer();
188 }
189 /**
190 * Render the theme header for classic or block themes.
191 *
192 * @return string
193 *
194 * @since 1.0.0
195 */
196 public function render_header()
197 {
198 if ($this->is_block_theme()) {
199 $this->block_header = do_blocks(\sprintf('<!-- wp:template-part {"slug":"header","theme":"%s","tagName":"header","layout":{"inherit":true}} /-->', esc_attr(get_stylesheet())));
200 $this->block_footer = do_blocks(\sprintf('<!-- wp:template-part {"slug":"footer","theme":"%s","tagName":"footer","className":"site-footer","layout":{"inherit":true}} /-->', esc_attr(get_stylesheet())));
201 \ob_start();
202 ?>
203 <!doctype html>
204 <html <?php
205 language_attributes();
206 ?>>
207 <head>
208 <meta charset="<?php
209 bloginfo('charset');
210 ?>">
211 <?php
212 wp_head();
213 ?>
214 </head>
215 <body <?php
216 body_class();
217 ?>>
218 <?php
219 wp_body_open();
220 ?>
221 <div class="wp-site-blocks">
222 <?php
223 return (string) \ob_get_clean() . $this->block_header;
224 }
225 \ob_start();
226 get_header();
227 return (string) \ob_get_clean();
228 }
229 /**
230 * Render the theme footer for classic or block themes.
231 *
232 * @return string
233 *
234 * @since 1.0.0
235 */
236 public function render_footer()
237 {
238 if ($this->is_block_theme()) {
239 \ob_start();
240 wp_footer();
241 $footer_scripts = (string) \ob_get_clean();
242 return $this->block_footer . '</div>' . $footer_scripts . '</body></html>';
243 }
244 \ob_start();
245 get_footer();
246 return (string) \ob_get_clean();
247 }
248 /**
249 * Include a view template file directly (without capturing output).
250 *
251 * @param string $view The view name in dot notation.
252 * @param array $data The data to pass to the view.
253 * @param bool $once Whether to load the template only once.
254 *
255 * @return void
256 *
257 * @throws RuntimeException When the view cannot be resolved.
258 *
259 * @since 1.0.0
260 */
261 public function include(string $view, array $data = [], bool $once = \true)
262 {
263 $path = $this->resolve_path($view);
264 if ($path === '') {
265 throw new RuntimeException(\sprintf('View [%s] not found.', $view));
266 }
267 $merged = \array_merge($this->shared, $data);
268 $context = app(ViewContext::class);
269 $context->push(['template' => $view, 'route_name' => '', 'resolved_path' => $path, 'data' => $merged]);
270 try {
271 if ($once) {
272 require_once $path;
273 return;
274 }
275 require $path;
276 } finally {
277 $context->pop();
278 }
279 }
280 /**
281 * Absolute path to the framework layout wrapper stub.
282 *
283 * @return string
284 *
285 * @since 2.1.2
286 */
287 public function layout_wrapper_path()
288 {
289 return \dirname(__FILE__) . '/layout-wrapper.php';
290 }
291 }
292