1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\View; |
4
|
|
|
|
5
|
|
|
use InvalidArgumentException; |
6
|
|
|
|
7
|
|
|
// From PSR-3 |
8
|
|
|
use Psr\Log\LoggerAwareInterface; |
9
|
|
|
use Psr\Log\LoggerAwareTrait; |
10
|
|
|
|
11
|
|
|
// From 'charcoal-view' |
12
|
|
|
use Charcoal\View\LoaderInterface; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Base Template Loader |
16
|
|
|
* |
17
|
|
|
* Finds a template file in a collection of directory paths. |
18
|
|
|
*/ |
19
|
|
|
abstract class AbstractLoader implements |
20
|
|
|
LoggerAwareInterface, |
21
|
|
|
LoaderInterface |
22
|
|
|
{ |
23
|
|
|
use LoggerAwareTrait; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var string |
27
|
|
|
*/ |
28
|
|
|
private $basePath = ''; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var string[] |
32
|
|
|
*/ |
33
|
|
|
private $paths = []; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var array |
37
|
|
|
*/ |
38
|
|
|
private $dynamicTemplates = []; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Default constructor, if none is provided by the concrete class implementations. |
42
|
|
|
* |
43
|
|
|
* ## Required dependencies |
44
|
|
|
* - `logger` A PSR-3 logger |
45
|
|
|
* |
46
|
|
|
* @param array $data The class dependencies map. |
47
|
|
|
*/ |
48
|
|
|
public function __construct(array $data = null) |
49
|
|
|
{ |
50
|
|
|
$this->setLogger($data['logger']); |
51
|
|
|
$this->setBasePath($data['base_path']); |
52
|
|
|
$this->setPaths($data['paths']); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Load a template content |
57
|
|
|
* |
58
|
|
|
* @deprecated $GLOBALS['widget_template'] |
59
|
|
|
* |
60
|
|
|
* @param string $ident The template ident to load and render. |
61
|
|
|
* @throws InvalidArgumentException If the dynamic template identifier is not a string. |
62
|
|
|
* @return string |
63
|
|
|
*/ |
64
|
|
|
public function load($ident) |
65
|
|
|
{ |
66
|
|
|
// Handle dynamic template |
67
|
|
|
if (substr($ident, 0, 1) === '$') { |
68
|
|
|
$tryLegacy = ($ident === '$widget_template'); |
69
|
|
|
|
70
|
|
|
$ident = $this->dynamicTemplate(substr($ident, 1)); |
71
|
|
|
|
72
|
|
|
// Legacy dynamic template hack |
73
|
|
|
if ($tryLegacy) { |
74
|
|
|
$ident = empty($GLOBALS['widget_template']) ? $ident : $GLOBALS['widget_template']; |
75
|
|
|
$this->logger->warning(sprintf( |
76
|
|
|
'%s is deprecated in favor of %s: %s', |
77
|
|
|
'$GLOBALS[\'widget_template\']', |
78
|
|
|
'setDynamicTemplate()', |
79
|
|
|
$ident |
80
|
|
|
)); |
81
|
|
|
if (!is_string($ident)) { |
82
|
|
|
throw new InvalidArgumentException( |
83
|
|
|
'Dynamic template ident (from "$widget_template") must be a string' |
84
|
|
|
); |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Prevents the loader from passing a proper template through further |
91
|
|
|
* procedures meant for a template identifier. |
92
|
|
|
*/ |
93
|
|
|
if ($this->isTemplateString($ident)) { |
94
|
|
|
return $ident; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
$file = $this->findTemplateFile($ident); |
98
|
|
|
if ($file === null || $file === '') { |
99
|
|
|
return $ident; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
return file_get_contents($file); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @param string $varName The name of the variable to get template ident from. |
107
|
|
|
* @throws InvalidArgumentException If the var name is not a string. |
108
|
|
|
* @return string |
109
|
|
|
*/ |
110
|
|
|
public function dynamicTemplate($varName) |
111
|
|
|
{ |
112
|
|
|
if (!is_string($varName)) { |
113
|
|
|
throw new InvalidArgumentException( |
114
|
|
|
'Can not get dynamic template: var name is not a string.' |
115
|
|
|
); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
if (!isset($this->dynamicTemplates[$varName])) { |
119
|
|
|
return ''; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return $this->dynamicTemplates[$varName]; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @deprecated $GLOBALS['widget_template'] |
127
|
|
|
* |
128
|
|
|
* @param string $varName The name of the variable to set this template unto. |
129
|
|
|
* @param string|null $templateIdent The "dynamic template" to set or NULL to clear. |
130
|
|
|
* @throws InvalidArgumentException If var name is not a string |
131
|
|
|
* or if the template is not a string (and not null). |
132
|
|
|
* @return void |
133
|
|
|
*/ |
134
|
|
|
public function setDynamicTemplate($varName, $templateIdent) |
135
|
|
|
{ |
136
|
|
|
if (!is_string($varName)) { |
137
|
|
|
throw new InvalidArgumentException( |
138
|
|
|
'Can not set dynamic template: var name is not a string.' |
139
|
|
|
); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
if ($templateIdent === null) { |
143
|
|
|
$this->removeDynamicTemplate($varName); |
|
|
|
|
144
|
|
|
return; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
if (!is_string($templateIdent)) { |
148
|
|
|
throw new InvalidArgumentException( |
149
|
|
|
'Can not set dynamic template. Must be a a string, or null.' |
150
|
|
|
); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
// Legacy dynamic template hack |
154
|
|
|
if ($varName === 'widget_template') { |
155
|
|
|
$GLOBALS['widget_template'] = $templateIdent; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$this->dynamicTemplates[$varName] = $templateIdent; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @deprecated $GLOBALS['widget_template'] |
163
|
|
|
* |
164
|
|
|
* @param string $varName The name of the variable to remove. |
165
|
|
|
* @throws InvalidArgumentException If var name is not a string. |
166
|
|
|
* @return void |
167
|
|
|
*/ |
168
|
|
|
public function removeDynamicTemplate($varName) |
169
|
|
|
{ |
170
|
|
|
if (!is_string($varName)) { |
171
|
|
|
throw new InvalidArgumentException( |
172
|
|
|
'Can not set dynamic template: var name is not a string.' |
173
|
|
|
); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
// Legacy dynamic template hack |
177
|
|
|
if ($varName === 'widget_template') { |
178
|
|
|
$GLOBALS['widget_template'] = null; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
unset($this->dynamicTemplates[$varName]); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @deprecated $GLOBALS['widget_template'] |
186
|
|
|
* |
187
|
|
|
* @return void |
188
|
|
|
*/ |
189
|
|
|
public function clearDynamicTemplates() |
190
|
|
|
{ |
191
|
|
|
// Legacy dynamic template hack |
192
|
|
|
$GLOBALS['widget_template'] = null; |
193
|
|
|
|
194
|
|
|
$this->dynamicTemplates = []; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* @return string |
199
|
|
|
*/ |
200
|
|
|
protected function basePath() |
201
|
|
|
{ |
202
|
|
|
return $this->basePath; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* @param string $basePath The base path to set. |
207
|
|
|
* @throws InvalidArgumentException If the base path parameter is not a string. |
208
|
|
|
* @return LoaderInterface Chainable |
209
|
|
|
*/ |
210
|
|
|
private function setBasePath($basePath) |
211
|
|
|
{ |
212
|
|
|
if (!is_string($basePath)) { |
213
|
|
|
throw new InvalidArgumentException( |
214
|
|
|
'Base path must be a string' |
215
|
|
|
); |
216
|
|
|
} |
217
|
|
|
$basePath = realpath($basePath); |
218
|
|
|
$this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR; |
219
|
|
|
return $this; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return string[] |
224
|
|
|
*/ |
225
|
|
|
protected function paths() |
226
|
|
|
{ |
227
|
|
|
return $this->paths; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param string[] $paths The list of path to add. |
232
|
|
|
* @return LoaderInterface Chainable |
233
|
|
|
*/ |
234
|
|
|
private function setPaths(array $paths) |
235
|
|
|
{ |
236
|
|
|
$this->paths = []; |
237
|
|
|
|
238
|
|
|
foreach ($paths as $path) { |
239
|
|
|
$this->addPath($path); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
return $this; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* @param string $path The path to add to the load. |
247
|
|
|
* @return LoaderInterface Chainable |
248
|
|
|
*/ |
249
|
|
|
private function addPath($path) |
250
|
|
|
{ |
251
|
|
|
$this->paths[] = $this->resolvePath($path); |
252
|
|
|
|
253
|
|
|
return $this; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* @param string $path The path to resolve. |
258
|
|
|
* @throws InvalidArgumentException If the path argument is not a string. |
259
|
|
|
* @return string |
260
|
|
|
*/ |
261
|
|
|
private function resolvePath($path) |
262
|
|
|
{ |
263
|
|
|
if (!is_string($path)) { |
264
|
|
|
throw new InvalidArgumentException( |
265
|
|
|
'Path needs to be a string' |
266
|
|
|
); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
$basePath = $this->basePath(); |
270
|
|
|
$path = rtrim($path, '/\\').DIRECTORY_SEPARATOR; |
271
|
|
|
if ($basePath && strpos($path, $basePath) === false) { |
272
|
|
|
$path = $basePath.$path; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
return $path; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Determine if the variable is a template literal. |
280
|
|
|
* |
281
|
|
|
* This method looks for any line-breaks in the given string, |
282
|
|
|
* which a file path would not allow. |
283
|
|
|
* |
284
|
|
|
* @param string $ident The template being evaluated. |
285
|
|
|
* @return boolean Returns TRUE if the given value is most likely the template contents |
286
|
|
|
* as opposed to a template identifier (file path). |
287
|
|
|
*/ |
288
|
|
|
protected function isTemplateString($ident) |
289
|
|
|
{ |
290
|
|
|
return strpos($ident, PHP_EOL) !== false; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Get the template file (full path + filename) to load from an ident. |
295
|
|
|
* |
296
|
|
|
* This method first generates the filename for an identifier and search for it in all of the loader's paths. |
297
|
|
|
* |
298
|
|
|
* @param string $ident The template identifier to load. |
299
|
|
|
* @throws InvalidArgumentException If the template ident is not a string. |
300
|
|
|
* @return string|null The full path + filename of the found template. Null if nothing was found. |
301
|
|
|
*/ |
302
|
|
|
protected function findTemplateFile($ident) |
303
|
|
|
{ |
304
|
|
|
if (!is_string($ident)) { |
305
|
|
|
throw new InvalidArgumentException(sprintf( |
306
|
|
|
'Template ident must be a string, received %s', |
307
|
|
|
is_object($ident) ? get_class($ident) : gettype($ident) |
308
|
|
|
)); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
$filename = $this->filenameFromIdent($ident); |
312
|
|
|
$searchPath = $this->paths(); |
313
|
|
|
foreach ($searchPath as $path) { |
314
|
|
|
$f = realpath($path).'/'.strtolower($filename); |
315
|
|
|
if (file_exists($f)) { |
316
|
|
|
return $f; |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
return null; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* @param string $ident The template identifier to convert to a filename. |
325
|
|
|
* @return string |
326
|
|
|
*/ |
327
|
|
|
abstract protected function filenameFromIdent($ident); |
328
|
|
|
} |
329
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.