|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* Handler class |
|
4
|
|
|
* |
|
5
|
|
|
* The handler holds an Implementation and controls recording for it. |
|
6
|
|
|
* Filtering for variables also happens here. |
|
7
|
|
|
* |
|
8
|
|
|
* @package Stencil |
|
9
|
|
|
*/ |
|
10
|
|
|
|
|
11
|
|
|
/** |
|
12
|
|
|
* Class Stencil_Handler |
|
13
|
|
|
*/ |
|
14
|
|
|
class Stencil_Handler implements Stencil_Handler_Interface, Stencil_Implementation_Interface { |
|
15
|
|
|
|
|
16
|
|
|
/** |
|
17
|
|
|
* Use ob_start / ob_end_clean to record a variable. |
|
18
|
|
|
* |
|
19
|
|
|
* @var string |
|
20
|
|
|
*/ |
|
21
|
|
|
protected $recording_for = null; |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* Revert to recorder after finishing recording. |
|
25
|
|
|
* |
|
26
|
|
|
* @var null|Stencil_Recorder_Interface |
|
27
|
|
|
*/ |
|
28
|
|
|
protected $revert_recorder_to; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* The instance of the proxy |
|
32
|
|
|
* |
|
33
|
|
|
* The proxy communicates with the engine |
|
34
|
|
|
* |
|
35
|
|
|
* @var Stencil_Implementation $proxy |
|
36
|
|
|
*/ |
|
37
|
|
|
protected $proxy; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* Recorder instance |
|
41
|
|
|
* |
|
42
|
|
|
* @var Stencil_Recorder_Interface |
|
43
|
|
|
*/ |
|
44
|
|
|
protected $recorder; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Control if the wp_head and wp_footer are being loaded |
|
48
|
|
|
* |
|
49
|
|
|
* Default disabled, Stencil will enable this for the core handler |
|
50
|
|
|
* |
|
51
|
|
|
* @var bool |
|
52
|
|
|
*/ |
|
53
|
|
|
protected $load_wp_header_and_footer = false; |
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* StencilHandler constructor. |
|
57
|
|
|
* |
|
58
|
|
|
* @param Stencil_Implementation $implementation Implementation to use. |
|
59
|
|
|
* @param Stencil_Recorder_Interface|null $recorder Recorder to use. |
|
60
|
|
|
*/ |
|
61
|
|
|
public function __construct( Stencil_Implementation $implementation, Stencil_Recorder_Interface $recorder = null ) { |
|
62
|
|
|
$this->proxy = $implementation; |
|
63
|
|
|
$this->recorder = is_null( $recorder ) ? new Stencil_Recorder() : $recorder; |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* Control loading the wp_head and wp_footer into variable |
|
68
|
|
|
* |
|
69
|
|
|
* @param bool|true $yes_or_no Wheter to load them or not. |
|
70
|
|
|
*/ |
|
71
|
|
|
public function load_wp_header_and_footer( $yes_or_no = true ) { |
|
72
|
|
|
if ( $this->load_wp_header_and_footer === $yes_or_no ) { |
|
73
|
|
|
return; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
if ( $yes_or_no ) { |
|
77
|
|
|
$this->hook_wp_header_and_footer(); |
|
78
|
|
|
} else { |
|
79
|
|
|
$this->unhook_wp_header_and_footer(); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
$this->load_wp_header_and_footer = (bool) $yes_or_no; |
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Get the used implementation |
|
87
|
|
|
* |
|
88
|
|
|
* @return Stencil_Implementation |
|
89
|
|
|
*/ |
|
90
|
|
|
public function get_implementation() { |
|
91
|
|
|
return $this->proxy; |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* Get the default recorder |
|
96
|
|
|
* |
|
97
|
|
|
* @return Stencil_Recorder_Interface |
|
98
|
|
|
*/ |
|
99
|
|
|
public function get_recorder() { |
|
100
|
|
|
return $this->recorder; |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
/** |
|
104
|
|
|
* Set the new default Recorder |
|
105
|
|
|
* |
|
106
|
|
|
* @param Stencil_Recorder_Interface $recorder The new Recorder to use as default. |
|
107
|
|
|
*/ |
|
108
|
|
|
public function set_recorder( Stencil_Recorder_Interface $recorder ) { |
|
109
|
|
|
$this->recorder = $recorder; |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Get the engine from the implementation |
|
114
|
|
|
*/ |
|
115
|
|
|
public function get_engine() { |
|
116
|
|
|
return $this->get_implementation()->get_engine(); |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
/** |
|
120
|
|
|
* Find the view that is implemented |
|
121
|
|
|
* |
|
122
|
|
|
* @param array|null $options Options that were collected from the hierarchy. |
|
123
|
|
|
* |
|
124
|
|
|
* @return string |
|
125
|
|
|
*/ |
|
126
|
|
|
public function get_usable_view( array $options = null ) { |
|
127
|
|
|
/** |
|
128
|
|
|
* Clean up |
|
129
|
|
|
*/ |
|
130
|
|
|
$options = array_unique( $options ); |
|
131
|
|
|
$options = array_filter( $options ); |
|
132
|
|
|
|
|
133
|
|
|
if ( empty( $options ) ) { |
|
134
|
|
|
return 'index'; |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
$implementation = $this->get_implementation(); |
|
138
|
|
|
|
|
139
|
|
|
$base = $implementation->get_template_path(); |
|
140
|
|
|
$base = ! is_array( $base ) ? array( $base ) : $base; |
|
141
|
|
|
|
|
142
|
|
|
foreach ( $options as $option ) { |
|
143
|
|
|
foreach ( $base as $root ) { |
|
144
|
|
|
if ( is_file( $root . $option . '.' . $implementation->get_template_extension() ) ) { |
|
145
|
|
|
return $option; |
|
146
|
|
|
} |
|
147
|
|
|
} |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
return 'index'; |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
/** |
|
154
|
|
|
* Sets a variable to the template engine |
|
155
|
|
|
* |
|
156
|
|
|
* @param string $variable Name of the variable. |
|
157
|
|
|
* @param mixed $value Value to assign to the variable. |
|
158
|
|
|
* @param bool $override Optional. Allowed override the variable if already set. |
|
159
|
|
|
* |
|
160
|
|
|
* @return mixed Template known value of the provided variable name |
|
161
|
|
|
*/ |
|
162
|
|
|
public function set( $variable, $value, $override = true ) { |
|
163
|
|
|
if ( ! $override ) { |
|
164
|
|
|
$current = $this->get( $variable ); |
|
165
|
|
|
|
|
166
|
|
|
// Exit if variable has a value. |
|
167
|
|
|
if ( ! is_null( $current ) ) { |
|
168
|
|
|
return $current; |
|
169
|
|
|
} |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
/** |
|
173
|
|
|
* Filter: stencil:set |
|
174
|
|
|
* |
|
175
|
|
|
* Allows for global filtering of template data |
|
176
|
|
|
* Questionable if this is the place to do this but |
|
177
|
|
|
* you never know what people would use this for.. |
|
178
|
|
|
*/ |
|
179
|
|
|
$value = Stencil_Environment::filter( 'set', $value, $variable ); |
|
180
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* Filter: stencil:set-{variable_name} |
|
183
|
|
|
* |
|
184
|
|
|
* Allows for global filtering of template data |
|
185
|
|
|
* Questionable if this is the place to do this but |
|
186
|
|
|
* you never know what people would use this for.. |
|
187
|
|
|
*/ |
|
188
|
|
|
$value = Stencil_Environment::filter( 'set-' . $variable, $value ); |
|
189
|
|
|
|
|
190
|
|
|
/** |
|
191
|
|
|
* Set the variable |
|
192
|
|
|
*/ |
|
193
|
|
|
$this->get_implementation()->set( $variable, $value ); |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* Return the value of the variable in the engine |
|
197
|
|
|
*/ |
|
198
|
|
|
return $this->get( $variable ); |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* Gets a variable from the template engine |
|
203
|
|
|
* |
|
204
|
|
|
* @param string $variable Name of the variable to get. |
|
205
|
|
|
* |
|
206
|
|
|
* @return mixed |
|
207
|
|
|
*/ |
|
208
|
|
|
public function get( $variable ) { |
|
209
|
|
|
return $this->get_implementation()->get( $variable ); |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
/** |
|
213
|
|
|
* Displays a chosen template |
|
214
|
|
|
* |
|
215
|
|
|
* Uses fetch to fetch the output |
|
216
|
|
|
* |
|
217
|
|
|
* @param string $template Template file to use. |
|
218
|
|
|
*/ |
|
219
|
|
|
public function display( $template ) { |
|
220
|
|
|
$this->internal_fetch( $template, 'display' ); |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
/** |
|
224
|
|
|
* Build and display the template |
|
225
|
|
|
* |
|
226
|
|
|
* @param string $template Template file to fetch. |
|
227
|
|
|
* |
|
228
|
|
|
* @return bool|string|void |
|
229
|
|
|
*/ |
|
230
|
|
|
public function fetch( $template ) { |
|
231
|
|
|
return $this->internal_fetch( $template, 'fetch' ); |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* Set the wp_head and wp_footer variables |
|
236
|
|
|
* |
|
237
|
|
|
* This function is being triggered by |
|
238
|
|
|
* stencil.pre_display and stencil.pre_fetch |
|
239
|
|
|
* so it can be disabled if preferred |
|
240
|
|
|
*/ |
|
241
|
|
|
public function set_wp_head_footer() { |
|
242
|
|
|
/** |
|
243
|
|
|
* Could split this up in a head/footer |
|
244
|
|
|
* but when the header is required the footer |
|
245
|
|
|
* finishes the complete scope. |
|
246
|
|
|
*/ |
|
247
|
|
|
|
|
248
|
|
|
$this->start_recording( 'wp_head' ); |
|
249
|
|
|
wp_head(); |
|
250
|
|
|
$this->finish_recording(); |
|
251
|
|
|
|
|
252
|
|
|
$this->start_recording( 'wp_footer' ); |
|
253
|
|
|
wp_footer(); |
|
254
|
|
|
$this->finish_recording(); |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
/** |
|
258
|
|
|
* Recorder for inline HTML cathing |
|
259
|
|
|
* |
|
260
|
|
|
* @param string $variable Variable to record into. |
|
261
|
|
|
* @param Stencil_Recorder_Interface|null $temporary_recorder Optional. Recorder to use for this recording. |
|
262
|
|
|
* |
|
263
|
|
|
* @throws Exception When already recording for other variable. |
|
264
|
|
|
* @throws InvalidArgumentException When the variable name is not a string. |
|
265
|
|
|
*/ |
|
266
|
|
|
public function start_recording( $variable, Stencil_Recorder_Interface $temporary_recorder = null ) { |
|
267
|
|
|
/** |
|
268
|
|
|
* Throw exception or error? |
|
269
|
|
|
*/ |
|
270
|
|
|
if ( ! empty( $this->recording_for ) ) { |
|
271
|
|
|
throw new Exception( sprintf( 'Already recording variable "%s".', $this->recording_for ) ); |
|
272
|
|
|
} |
|
273
|
|
|
|
|
274
|
|
|
if ( ! is_string( $variable ) || empty( $variable ) ) { |
|
275
|
|
|
throw new InvalidArgumentException( 'Expected variable name to record for.' ); |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
|
|
// Set temp recorder as active. |
|
279
|
|
|
if ( ! is_null( $temporary_recorder ) ) { |
|
280
|
|
|
$swap = $this->recorder; |
|
281
|
|
|
$this->recorder = $temporary_recorder; |
|
282
|
|
|
$this->revert_recorder_to = $swap; |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
$this->recording_for = $variable; |
|
286
|
|
|
$this->recorder->start_recording(); |
|
287
|
|
|
} |
|
288
|
|
|
|
|
289
|
|
|
/** |
|
290
|
|
|
* Finish recording raw input |
|
291
|
|
|
* |
|
292
|
|
|
* @return mixed |
|
293
|
|
|
* @throws Exception When we are not recording. |
|
294
|
|
|
*/ |
|
295
|
|
|
public function finish_recording() { |
|
296
|
|
|
if ( is_null( $this->recording_for ) ) { |
|
297
|
|
|
throw new Exception( 'Not recording.' ); |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
$this->recorder->finish_recording(); |
|
301
|
|
|
$recording = $this->recorder->get_recording(); |
|
302
|
|
|
|
|
303
|
|
|
$this->set( $this->recording_for, $recording ); |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* Re-set original recorder |
|
307
|
|
|
*/ |
|
308
|
|
|
if ( isset( $this->revert_recorder_to ) ) { |
|
309
|
|
|
$this->recorder = $this->revert_recorder_to; |
|
310
|
|
|
unset( $this->revert_recorder_to ); |
|
311
|
|
|
} |
|
312
|
|
|
|
|
313
|
|
|
/** |
|
314
|
|
|
* Clear variable holder |
|
315
|
|
|
*/ |
|
316
|
|
|
$this->recording_for = null; |
|
317
|
|
|
|
|
318
|
|
|
return $recording; |
|
319
|
|
|
} |
|
320
|
|
|
|
|
321
|
|
|
/** |
|
322
|
|
|
* Unified function for fetching and displaying a template |
|
323
|
|
|
* |
|
324
|
|
|
* @param string $template Template file to load. |
|
325
|
|
|
* @param string $from Source of this request. |
|
326
|
|
|
* |
|
327
|
|
|
* @return mixed|WP_Error |
|
328
|
|
|
* |
|
329
|
|
|
* @throws LogicException When we are still recording a variable. |
|
330
|
|
|
*/ |
|
331
|
|
|
protected function internal_fetch( $template, $from ) { |
|
332
|
|
|
if ( ! is_null( $this->recording_for ) ) { |
|
333
|
|
|
throw new LogicException( sprintf( 'Stencil: trying to fetch view %s but still recording for "%s".', $template, $this->recording_for ) ); |
|
334
|
|
|
} |
|
335
|
|
|
|
|
336
|
|
|
$implementation = $this->get_implementation(); |
|
337
|
|
|
|
|
338
|
|
|
// Hook pre_fetch / pre_display. |
|
339
|
|
|
Stencil_Environment::trigger( 'pre_' . $from, $template ); |
|
340
|
|
|
|
|
341
|
|
|
// Make sure undefined index errors are not caught; template engines don't check for these. |
|
342
|
|
|
$error_reporting = error_reporting(); |
|
343
|
|
|
error_reporting( error_reporting() & ~E_NOTICE ); |
|
344
|
|
|
|
|
345
|
|
|
// Fetch. |
|
346
|
|
|
$fetched = $implementation->fetch( $template . '.' . $implementation->get_template_extension() ); |
|
347
|
|
|
|
|
348
|
|
|
// Restore error_reporting. |
|
349
|
|
|
error_reporting( $error_reporting ); |
|
350
|
|
|
|
|
351
|
|
|
/** |
|
352
|
|
|
* Apply filtering |
|
353
|
|
|
*/ |
|
354
|
|
|
$fetched = Stencil_Environment::filter( 'content', $fetched ); |
|
355
|
|
|
|
|
356
|
|
|
/** |
|
357
|
|
|
* Echo if we are displaying |
|
358
|
|
|
*/ |
|
359
|
|
|
if ( 'display' === $from ) { |
|
360
|
|
|
echo $fetched; |
|
361
|
|
|
} |
|
362
|
|
|
|
|
363
|
|
|
// Hook post_fetch / post_display. |
|
364
|
|
|
Stencil_Environment::trigger( 'post_' . $from, $template ); |
|
365
|
|
|
|
|
366
|
|
|
if ( 'fetch' === $from ) { |
|
367
|
|
|
return $fetched; |
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
return ''; |
|
371
|
|
|
} |
|
372
|
|
|
|
|
373
|
|
|
/** |
|
374
|
|
|
* Attach hooks to load the wp_head and wp_footer variables |
|
375
|
|
|
*/ |
|
376
|
|
View Code Duplication |
private function hook_wp_header_and_footer() { |
|
377
|
|
|
/** |
|
378
|
|
|
* Add wp_head and wp_footer variable recording |
|
379
|
|
|
*/ |
|
380
|
|
|
add_action( Stencil_Environment::format_hook( 'pre_display' ), array( $this, 'set_wp_head_footer' ) ); |
|
381
|
|
|
add_action( Stencil_Environment::format_hook( 'pre_fetch' ), array( $this, 'set_wp_head_footer' ) ); |
|
382
|
|
|
} |
|
383
|
|
|
|
|
384
|
|
|
/** |
|
385
|
|
|
* Detach hooks to load the wp_head and wp_footer variables |
|
386
|
|
|
*/ |
|
387
|
|
View Code Duplication |
private function unhook_wp_header_and_footer() { |
|
388
|
|
|
/** |
|
389
|
|
|
* Add wp_head and wp_footer variable recording |
|
390
|
|
|
*/ |
|
391
|
|
|
remove_action( Stencil_Environment::format_hook( 'pre_display' ), array( $this, 'set_wp_head_footer' ) ); |
|
392
|
|
|
remove_action( Stencil_Environment::format_hook( 'pre_fetch' ), array( $this, 'set_wp_head_footer' ) ); |
|
393
|
|
|
} |
|
394
|
|
|
} |
|
395
|
|
|
|