-
Notifications
You must be signed in to change notification settings - Fork 1
/
TemplateX.php
350 lines (309 loc) · 8.73 KB
/
TemplateX.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
<?php
namespace wpscholar;
/**
* Class TemplateX
*
* @package wpscholar
*/
class TemplateX {
/**
* Relative path to the template file to be loaded.
*
* @var string
*/
protected $template;
/**
* Paths to the template directory; will be checked in order.
*
* @var array
*/
protected $templatePaths = [];
/**
* An associative array containing variable names and values to be scoped to the current template.
*
* @var array
*/
protected $vars = [];
/**
* TemplateX constructor.
*
* @param array $templatePaths Paths to the template directory; will be checked in order.
*/
public function __construct( array $templatePaths = [] ) {
$this->templatePaths = $templatePaths;
}
/**
* Set the template.
*
* @param string $template Relative path to the template file to be loaded.
*/
public function setTemplate( $template ) {
$this->template = $template;
}
/**
* Set the template paths.
*
* @param array $paths Paths to the template directory; will be checked in order.
*/
public function setTemplatePaths( array $paths ) {
$this->templatePaths = $paths;
}
/**
* Add a template path.
*
* @param string $path Path to be added to the array of template paths.
*/
public function addTemplatePath( $path ) {
$this->templatePaths[] = $path;
}
/**
* Set the context.
*
* @param array $vars An associative array containing variable names and values to be scoped to the current template.
*/
public function setContext( array $vars = [] ) {
$this->vars = $vars;
}
/**
* Checks if a variable exists.
*
* @param string $property Name of the variable to check.
*
* @return bool Whether or not the variable exists.
*/
public function has( $property ) {
return $this->hasIn( $this->vars, $property );
}
/**
* Check if a value exists in an object or an array. Allows the ability to check a nested value
* by defining a path using an array or dot notation.
*
* @param object|array $data
* @param string|array $path
*
* @return bool
*/
public function hasIn( $data, $path ) {
$value = false;
if ( \is_array( $data ) && array_key_exists( $path, $data ) ) {
$value = true;
} else if ( \is_object( $data ) && property_exists( $data, $path ) ) {
$value = true;
} else if ( \is_array( $path ) || ( \is_string( $path ) && false !== strpos( $path, '.' ) ) ) {
$segments = \is_array( $path ) ? $path : explode( '.', $path );
foreach ( $segments as $segment ) {
if ( \is_array( $data ) && array_key_exists( $segment, $data ) ) {
$value = true;
$data = $data[ $segment ];
} else if ( \is_object( $data ) && property_exists( $data, $segment ) ) {
$value = true;
$data = $data->{$segment};
} else {
$value = false;
break;
}
}
}
return $value;
}
/**
* Get a variable.
*
* @param string|array $property Name (or path) of the variable to be fetched.
* @param mixed $default The default value to be returned if variable doesn't exist.
*
* @return mixed Value of the variable being fetched.
*/
public function get( $property, $default = null ) {
return $this->getIn( $this->vars, $property, $default );
}
/**
* Get a value from an object or an array. Allows the ability to fetch a nested value
* by defining a path using an array or dot notation.
*
* @param object|array $data An object or array containing the data.
* @param string|array $path The path in which to check for a specific value.
* @param mixed $default The default value to return if the value is not found.
*
* @return mixed The value, if found, or the default value otherwise
*/
public function getIn( $data, $path, $default = null ) {
$value = $default;
if ( \is_array( $data ) && array_key_exists( $path, $data ) ) {
$value = $data[ $path ];
} else if ( \is_object( $data ) && property_exists( $data, $path ) ) {
$value = $data->{$path};
} else if ( \is_array( $path ) || ( \is_string( $path ) && false !== strpos( $path, '.' ) ) ) {
$segments = \is_array( $path ) ? $path : explode( '.', $path );
foreach ( $segments as $segment ) {
if ( \is_array( $data ) && array_key_exists( $segment, $data ) ) {
$value = $data = $data[ $segment ];
} else if ( \is_object( $data ) && property_exists( $data, $segment ) ) {
$value = $data = $data->{$segment};
} else {
$value = $default;
break;
}
}
}
return $value;
}
/**
* Set a variable.
*
* @param string $property Name of the variable to be set.
* @param mixed $value Value of the variable.
*/
public function set( $property, $value ) {
$this->vars = $this->setIn( $this->vars, $property, $value );
}
/**
* Set a value in an object or an array. Allows the ability to set a nested value
* by defining a path using an array or dot notation.
*
* @param object|array $data An object or array containing the data
* @param string $path The path in which to set the value.
* @param mixed $value The value to be assigned.
*
* @return object|array Returns the updated value on success or the original value on failure.
*/
public function setIn( $data, $path, $value ) {
if ( ! \is_array( $path ) || ! ( \is_string( $path ) && false !== strpos( $path, '.' ) ) ) {
if ( \is_object( $data ) ) {
$data->{$path} = $value;
} else if ( is_array( $data ) ) {
$data[ $path ] = $value;
} else {
trigger_error(
sprintf( 'ERROR: Unable to set value on data type % in %', gettype( $data ), __METHOD__ ),
E_USER_WARNING
);
}
} else {
$segments = \is_array( $path ) ? $path : explode( '.', $path );
$segment = array_shift( $segments );
if ( empty( $segments ) ) {
$this->setIn( $data, $segment, $value );
} else {
if ( ! isset( $data[ $segment ], $data->{$segment} ) ) {
$data[ $segment ] = [];
}
$this->setIn( $data[ $segment ], implode( '.', $segments ), $value );
}
}
return $data;
}
/**
* Check if a variable has a specific value.
*
* @param string $property Name of the variable to check.
* @param mixed $value Value for which to check.
*
* @return bool Whether or not the values match.
*/
public function is( $property, $value ) {
return $this->get( $property ) == $value;
}
/**
* Unset a variable by name.
*
* @param string $property Variable to be deleted.
*/
public function delete( $property ) {
unset( $this->vars[ $property ] );
}
/**
* Loads a template from within an existing template.
*
* @param string $template Relative path to the template file to be loaded.
* @param array $vars An associative array containing variable names and values to be scoped to the current template.
* @param bool $withContext Whether or not to keep the existing context when loading the template.
* @param bool $echo Whether or not to echo the output (as opposed to returning the output).
*
* @return string
*/
public function load( $template, array $vars = [], $withContext = true, $echo = true ) {
if ( $withContext ) {
$vars = array_merge( $this->vars, $vars );
}
$x = new self();
$x->setTemplatePaths( $this->templatePaths );
$x->setTemplate( $template );
$x->setContext( $vars );
$output = $x->render();
if ( ! $echo ) {
return $output;
}
echo $output;
}
/**
* Get rendered template as a string.
*
* @return string Template rendered to a string.
*/
public function render() {
$output = '';
$template = $this->locateTemplate();
if ( $template ) {
$vars = array_merge( $this->vars, [ 'x' => $this ] );
extract( $vars, EXTR_SKIP );
ob_start();
include $template;
$output = ob_get_clean();
}
return $output;
}
/**
* Locate a template.
*
* @return string The full path to the template or an empty string if not found.
*/
protected function locateTemplate() {
$template = '';
foreach ( $this->templatePaths as $path ) {
$file = rtrim( $path, '/' ) . '/' . $this->template;
if ( file_exists( $file ) ) {
$template = $file;
break;
}
}
return $template;
}
/**
* Magic get method.
*
* @param string $property Name of the variable to get.
*
* @return mixed Value of the variable.
*/
public function __get( $property ) {
return $this->get( $property );
}
/**
* Magic set method.
*
* @param string $property Name of the variable to set.
* @param mixed $value Value of the variable.
*/
public function __set( $property, $value ) {
$this->set( $property, $value );
}
/**
* Magic isset method.
*
* @param string $property Name of the variable to check.
*
* @return bool Whether or not the variable is set.
*/
public function __isset( $property ) {
return $this->has( $property );
}
/**
* Convert class instance to a string.
*
* @return string Template rendered to a string.
*/
public function __toString() {
return $this->render();
}
}