-
Notifications
You must be signed in to change notification settings - Fork 1
/
TplBlock.php
386 lines (336 loc) · 9.32 KB
/
TplBlock.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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
<?php
/**
* The TplBlock class.
*
* @category Template
* @package TplBlock
* @author gnieark <[email protected]>
* @license GNU General Public License V3
* @link https://github.com/gnieark/tplBlock/
*/
class TplBlock
{
/**
* The string starting a block start.
*
* @var string
*/
const BLOCKSTARTSTART = '<!--\s+BEGIN\s+';
/**
* The string ending a block start.
*
* @var string
*/
const BLOCKSTARTEND = '\s+-->';
/**
* The string starting a block end.
*
* @var string
*/
const BLOCKENDSTART = '<!--\s+END\s+';
/**
* The string ending a block end.
*
* @var string
*/
const BLOCKENDEND = '\s+-->';
/**
* The string starting an enclosure.
*
* @var string
*/
const STARTENCLOSURE = '{{';
/**
* The string ending an enclosure.
*
* @var string
*/
const ENDENCLOSURE = '}}';
/**
* The name of the block.
*
* @var string
*/
public $name = '';
/**
* The array containing the variables used by TplBlock.
*
* @var array
*/
private $vars = [];
/**
* The array containing the sub blocks.
*
* @var array
*/
private $subBlocs = [];
/**
* The regex recognizing that a block is unused.
*
* @var string
*/
private $unusedRegex = "";
/**
* Should we trim?
*
* @var boolean
*/
private $trim = true;
/**
* Should we replace non set template vars by an empty string?
*
* @var boolean
*/
private $replaceNonGivenVars = true;
/**
* Use strict mode?
*
* @var boolean
*/
private $strictMode = true;
/**
* Initialize TplBlock
*
* The name can be empty only for the top one block.
*
* @param string $name The template name
*/
public function __construct($name = "")
{
// Checks that name is valid.
if ($name !== "" and ! ctype_alnum($name)) {
throw new \UnexpectedValueException(
"Only alpha-numerics chars are allowed on the block name"
);
}
$this->name = $name;
// Build the unused regex.
$this->unusedRegex = '/'
. self::BLOCKSTARTSTART
. ' *([a-z][a-z0-9.]*) *'
. self::BLOCKSTARTEND
. '(.*?)'
. self::BLOCKENDSTART
. ' *\1 *'
. self::BLOCKENDEND
. '/is'
;
}
/**
* Add simple variables
*
* The array must be structured like this:
*
* [ "key" => "value", "key2" => "value2" ]
*
* @param array $vars Variables to add.
*
* @return TplBlock For chaining.
*/
public function addVars(array $vars)
{
$this->vars = array_merge($this->vars, $vars);
return $this;
}
/**
* Add a sub block.
*
* @param TplBlock $bloc The block to add as a sub block.
*
* @return TplBlock For chaining.
*/
public function addSubBlock(TplBlock $bloc)
{
// An unnamed block cannot be a sub block.
if ($bloc->name === "") {
throw new \UnexpectedValueException(
"A sub tpl block can't have an empty name"
);
}
$this->subBlocs[$bloc->name][] = $bloc;
return $this;
}
public static function is_assoc($arr){
if(!is_array($arr)){
return false;
}
return array_keys($arr) !== range(0, count($arr) - 1);
}
/**
* Automatically add subs blocs and sub sub blocs ..., and vars
* directly from an associative array
* @param $subBlocsDefinitions the associative array
* @return TplBlock For chaining.
*/
public function addSubBlocsDefinitions($subBlocsDefinitions)
{
foreach($subBlocsDefinitions as $itemKey => $itemValue){
if(self::is_assoc($itemValue)){
$subBloc = new TplBlock($itemKey);
$subBloc->addSubBlocsDefinitions($itemValue);
$this->addSubBlock($subBloc);
}elseif(is_array($itemValue)){
foreach($itemValue as $subItem){
$subBloc = new TplBlock($itemKey);
$subBloc->addSubBlocsDefinitions($subItem);
$this->addSubBlock($subBloc);
}
}else{
$this->addVars(array($itemKey => $itemValue));
}
}
return $this;
}
/**
* Generate the sub block regex.
*
* @param string $prefix The prefix to add to the block name.
* @param string $blocName The block name.
*
* @return string The regex.
*/
private function subBlockRegex($prefix, $blocName)
{
return '/'
. self::BLOCKSTARTSTART
. preg_quote($prefix . $blocName)
. self::BLOCKSTARTEND
. ($this->trim === false ? '' : '(?:\R|)?' )
. '(.*?)'
. ($this->trim === false ? '' : '(?:\R|)?' )
. self::BLOCKENDSTART
. preg_quote($prefix . $blocName)
. self::BLOCKENDEND
. '/is';
}
/**
* Shake the template string and input vars then returns the parsed text.
*
* @param string $str containing the template to parse
* @param string $subBlocsPath optional, for this class internal use.
* The path should look like "bloc.subbloc".
*
* @return string The processed output.
*/
public function applyTplStr($str, $subBlocsPath = "")
{
// Replace all simple vars.
$prefix = $subBlocsPath === "" ? "" : $subBlocsPath . ".";
foreach ($this->vars as $key => $value) {
$str = str_replace(
self::STARTENCLOSURE . $prefix . $key . self::ENDENCLOSURE,
$value,
$str
);
}
// Parse blocs.
foreach ($this->subBlocs as $blocName => $blocsArr) {
$str = preg_replace_callback(
$this->subBlockRegex($prefix, $blocName),
function ($m) use ($blocName, $blocsArr, $prefix) {
$out = "";
foreach ($blocsArr as $bloc) {
// Recursion.
$out .= $bloc->applyTplStr(
$m[1],
$prefix . $blocName
);
}
return $out;
},
$str
);
}
// Delete unused blocs.
$str = preg_replace($this->unusedRegex, "", $str);
//Replace non setted vars by empty string
if($this->replaceNonGivenVars) {
$str = preg_replace( "/" .self::STARTENCLOSURE .'([a-z][a-z0-9.]*)' .self::ENDENCLOSURE ."/", '', $str );
}
//check if loops patterns are still presents
if (($this->strictMode)
&& (
preg_match( "/".self::BLOCKSTARTSTART."/", $str)
|| preg_match( "/".self::BLOCKENDSTART."/", $str)
)
){
throw new \UnexpectedValueException("Template string not consistent");
}
return $str;
}
/**
* Load a file, and pass his content to applyTplStr function.
*
* @param string $file The file path of the template to load
*
* @return string The processed output.
*/
public function applyTplFile($file)
{
if (! $tplStr = file_get_contents($file)) {
throw new \UnexpectedValueException("Cannot read given file $file");
}
return $this->applyTplStr($tplStr, "");
}
/**
* Enables trimming.
*
* @return TplBlock For chaining.
*/
public function doTrim()
{
$this->trim = true;
return $this;
}
/**
* Disables trimming.
*
* @return TplBlock For chaining.
*/
public function dontTrim()
{
$this->trim = false;
return $this;
}
/**
* Enable the behaviour: Non given vars will be replaced by an empty string.
*
* @return TplBlock For chaining.
*/
public function doReplaceNonGivenVars()
{
$this->replaceNonGivenVars = true;
return $this;
}
/**
* Enable the behaviour: Non given vars will be replaced by an empty string.
*
* @return TplBlock For chaining.
*/
public function dontReplaceNonGivenVars()
{
$this->replaceNonGivenVars = false;
return $this;
}
/**
* Enable mode strict. If template is inconsistent, will throw an exception
* and return nothing
*
* @return TplBlock For chaining.
*/
public function doStrictMode()
{
$this->strictMode = true;
return $this;
}
/**
* Disable mode strict. If template is inconsistent, will be parsed anyway.
* and no errors will be returned.
*
* @return TplBlock For chaining.
*/
public function dontStrictMode(){
$this->strictMode = false;
return $this;
}
}