1
|
|
|
<?php
|
2
|
|
|
/**
|
3
|
|
|
* This file is part of the O2System Framework package.
|
4
|
|
|
*
|
5
|
|
|
* For the full copyright and license information, please view the LICENSE
|
6
|
|
|
* file that was distributed with this source code.
|
7
|
|
|
*
|
8
|
|
|
* @author Steeve Andrian Salim
|
9
|
|
|
* @copyright Copyright (c) Steeve Andrian Salim
|
10
|
|
|
*/
|
11
|
|
|
|
12
|
|
|
// ------------------------------------------------------------------------
|
13
|
|
|
|
14
|
|
|
namespace O2System\Parser\Template\Engines;
|
15
|
|
|
|
16
|
|
|
// ------------------------------------------------------------------------
|
17
|
|
|
|
18
|
|
|
use O2System\Parser\Template\Abstracts\AbstractEngine;
|
19
|
|
|
use O2System\Spl\Traits\Collectors\ConfigCollectorTrait;
|
20
|
|
|
|
21
|
|
|
/**
|
22
|
|
|
* Class Blade
|
23
|
|
|
*
|
24
|
|
|
* @package O2System\Parser\Template\Engines
|
25
|
|
|
*
|
26
|
|
|
* @todo :
|
27
|
|
|
* 1. @include (done)
|
28
|
|
|
* 2. @each (done)
|
29
|
|
|
* 3. @inject
|
30
|
|
|
* 4. @stack
|
31
|
|
|
* 5. @push
|
32
|
|
|
* 6. @verbatim
|
33
|
|
|
*/
|
34
|
|
|
class Blade extends AbstractEngine
|
35
|
|
|
{
|
36
|
|
|
use ConfigCollectorTrait;
|
37
|
|
|
|
38
|
|
|
/**
|
39
|
|
|
* Blade::$extensions
|
40
|
|
|
*
|
41
|
|
|
* List of blade file extensions.
|
42
|
|
|
*
|
43
|
|
|
* @var array
|
44
|
|
|
*/
|
45
|
|
|
protected $extensions = [
|
46
|
|
|
'.php',
|
47
|
|
|
'.blade.php',
|
48
|
|
|
'.phtml',
|
49
|
|
|
];
|
50
|
|
|
|
51
|
|
|
/**
|
52
|
|
|
* Blade::$vars
|
53
|
|
|
*
|
54
|
|
|
* Blade variables
|
55
|
|
|
*
|
56
|
|
|
* @var array
|
57
|
|
|
*/
|
58
|
|
|
protected $vars = [];
|
59
|
|
|
|
60
|
|
|
/**
|
61
|
|
|
* Blade::$sections
|
62
|
|
|
*
|
63
|
|
|
* List of blade sections.
|
64
|
|
|
*
|
65
|
|
|
* @var array
|
66
|
|
|
*/
|
67
|
|
|
protected $sections = [];
|
68
|
|
|
|
69
|
|
|
// ------------------------------------------------------------------------
|
70
|
|
|
|
71
|
|
|
/**
|
72
|
|
|
* Blade::__construct
|
73
|
|
|
*
|
74
|
|
|
* @param array $config
|
75
|
|
|
*/
|
76
|
|
|
public function __construct(array $config = [])
|
77
|
|
|
{
|
78
|
|
|
$this->config = array_merge([
|
79
|
|
|
'allowPhpGlobals' => true,
|
80
|
|
|
'allowPhpFunctions' => true,
|
81
|
|
|
'allowPhpConstants' => true,
|
82
|
|
|
], $config);
|
83
|
|
|
}
|
84
|
|
|
|
85
|
|
|
// ------------------------------------------------------------------------
|
86
|
|
|
|
87
|
|
|
/**
|
88
|
|
|
* Blade::parseString
|
89
|
|
|
*
|
90
|
|
|
* @param string $string
|
91
|
|
|
* @param array $vars
|
92
|
|
|
*
|
93
|
|
|
* @return bool|string
|
94
|
|
|
*/
|
95
|
|
|
public function parseString($string, array $vars = [])
|
96
|
|
|
{
|
97
|
|
|
$this->vars =& $vars;
|
98
|
|
|
|
99
|
|
|
// Collect sections with no closing
|
100
|
|
|
$string = preg_replace_callback('/@section((.*),(.*))/', [&$this, 'collectSection'], $string);
|
101
|
|
|
|
102
|
|
|
// Collect sections with @show closing
|
103
|
|
|
$string = preg_replace_callback(
|
104
|
|
|
'/@section(.*)\s+(.*)\s+@show/',
|
105
|
|
|
[&$this, 'collectSectionWithShow'],
|
106
|
|
|
$string
|
107
|
|
|
);
|
108
|
|
|
|
109
|
|
|
// Collect sections with @endsection closing
|
110
|
|
|
$string = preg_replace_callback(
|
111
|
|
|
'/@section(.*)\s+(.*)\s+@endsection/',
|
112
|
|
|
[&$this, 'collectSectionWithEnd'],
|
113
|
|
|
$string
|
114
|
|
|
);
|
115
|
|
|
|
116
|
|
|
// Collect sections with @stop closing
|
117
|
|
|
$string = preg_replace_callback(
|
118
|
|
|
'/@section(.*)\s+(.*)\s+@stop/',
|
119
|
|
|
[&$this, 'collectSectionWithEnd'],
|
120
|
|
|
$string
|
121
|
|
|
);
|
122
|
|
|
|
123
|
|
|
// Collect sections with @overwrite closing
|
124
|
|
|
$string = preg_replace_callback(
|
125
|
|
|
'/@section(.*)\s+(.*)\s+@overwrite/',
|
126
|
|
|
[&$this, 'collectSectionWithEnd'],
|
127
|
|
|
$string
|
128
|
|
|
);
|
129
|
|
|
|
130
|
|
|
// Collect sections with @parent
|
131
|
|
|
$string = preg_replace_callback(
|
132
|
|
|
'/@section(.*)\s+@parent\s+(.*)\s+@endsection/',
|
133
|
|
|
[&$this, 'collectSectionWithParent'],
|
134
|
|
|
$string
|
135
|
|
|
);
|
136
|
|
|
|
137
|
|
|
// Remove blank lines
|
138
|
|
|
$string = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $string);
|
139
|
|
|
|
140
|
|
|
return $this->replaceString($string);
|
141
|
|
|
}
|
142
|
|
|
|
143
|
|
|
// ------------------------------------------------------------------------
|
144
|
|
|
|
145
|
|
|
/**
|
146
|
|
|
* Blade::replaceString
|
147
|
|
|
*
|
148
|
|
|
* @param string $string
|
149
|
|
|
*
|
150
|
|
|
* @return bool|string
|
151
|
|
|
*/
|
152
|
|
|
private function replaceString($string)
|
153
|
|
|
{
|
154
|
|
|
if ($this->config[ 'allowPhpGlobals' ] === false) {
|
155
|
|
|
$string = str_replace(
|
156
|
|
|
[
|
157
|
|
|
'{{$GLOBALS}}',
|
158
|
|
|
'{{$GLOBALS[%%]}}',
|
159
|
|
|
'{{$_SERVER}}',
|
160
|
|
|
'{{$_SERVER[%%]}}',
|
161
|
|
|
'{{$_GET}}',
|
162
|
|
|
'{{$_GET[%%]}}',
|
163
|
|
|
'{{$_POST}}',
|
164
|
|
|
'{{$_POST[%%]}}',
|
165
|
|
|
'{{$_FILES}}',
|
166
|
|
|
'{{$_FILES[%%]}}',
|
167
|
|
|
'{{$_COOKIE}}',
|
168
|
|
|
'{{$_COOKIE[%%]}}',
|
169
|
|
|
'{{$_SESSION}}',
|
170
|
|
|
'{{$_SESSION[%%]}}',
|
171
|
|
|
'{{$_REQUEST}}',
|
172
|
|
|
'{{$_REQUEST[%%]}}',
|
173
|
|
|
'{{$_ENV}}',
|
174
|
|
|
'{{$_ENV[%%]}}',
|
175
|
|
|
|
176
|
|
|
// with spaces
|
177
|
|
|
'{{ $GLOBALS }}',
|
178
|
|
|
'{{ $GLOBALS[%%] }}',
|
179
|
|
|
'{{ $GLOBALS.%% }}',
|
180
|
|
|
'{{ $_SERVER }}',
|
181
|
|
|
'{{ $_SERVER[%%] }}',
|
182
|
|
|
'{{ $_SERVER.%% }}',
|
183
|
|
|
'{{ $_GET }}',
|
184
|
|
|
'{{ $_GET[%%] }}',
|
185
|
|
|
'{{ $_GET.%% }}',
|
186
|
|
|
'{{ $_POST }}',
|
187
|
|
|
'{{ $_POST[%%] }}',
|
188
|
|
|
'{{ $_POST.%% }}',
|
189
|
|
|
'{{ $_FILES }}',
|
190
|
|
|
'{{ $_FILES[%%] }}',
|
191
|
|
|
'{{ $_FILES.%% }}',
|
192
|
|
|
'{{ $_COOKIE }}',
|
193
|
|
|
'{{ $_COOKIE[%%] }}',
|
194
|
|
|
'{{ $_COOKIE.%% }}',
|
195
|
|
|
'{{ $_SESSION }}',
|
196
|
|
|
'{{ $_SESSION[%%] }}',
|
197
|
|
|
'{{ $_SESSION.%% }}',
|
198
|
|
|
'{{ $_REQUEST }}',
|
199
|
|
|
'{{ $_REQUEST[%%] }}',
|
200
|
|
|
'{{ $_REQUEST.%% }}',
|
201
|
|
|
'{{ $_ENV }}',
|
202
|
|
|
'{{ $_ENV[%%] }}',
|
203
|
|
|
'{{ $_ENV.%% }}',
|
204
|
|
|
],
|
205
|
|
|
'',
|
206
|
|
|
$string
|
207
|
|
|
);
|
208
|
|
|
}
|
209
|
|
|
|
210
|
|
|
// php logical codes
|
211
|
|
|
$logicalCodes = [
|
212
|
|
|
'@if(%%)' => '<?php if(\1): ?>',
|
213
|
|
|
'@elseif(%%)' => '<?php elseif(\1): ?>',
|
214
|
|
|
'@endif' => '<?php endif; ?>',
|
215
|
|
|
'@else' => '<?php else: ?>',
|
216
|
|
|
'@unless(%%)' => '<?php if(\1): ?>',
|
217
|
|
|
'@endunless' => '<?php endif; ?>',
|
218
|
|
|
|
219
|
|
|
// with spaces
|
220
|
|
|
'@if( %% )' => '<?php if(\1): ?>',
|
221
|
|
|
'@elseif( %% )' => '<?php elseif(\1): ?>',
|
222
|
|
|
'@unless( %% )' => '<?php if(\1): ?>',
|
223
|
|
|
'@if (%%)' => '<?php if(\1): ?>',
|
224
|
|
|
'@elseif (%%)' => '<?php elseif(\1): ?>',
|
225
|
|
|
'@unless (%%)' => '<?php if(\1): ?>',
|
226
|
|
|
'@if ( %% )' => '<?php if(\1): ?>',
|
227
|
|
|
'@elseif ( %% )' => '<?php elseif(\1): ?>',
|
228
|
|
|
'@unless ( %% )' => '<?php if(\1): ?>',
|
229
|
|
|
];
|
230
|
|
|
|
231
|
|
|
// php loop codes
|
232
|
|
|
$loopCodes = [
|
233
|
|
|
'@foreach(%%)' => '<?php foreach(\1): ?>',
|
234
|
|
|
'@endforeach' => '<?php endforeach; ?>',
|
235
|
|
|
'@for(%%)' => '<?php for(\1): ?>',
|
236
|
|
|
'@endfor' => '<?php endfor; ?>',
|
237
|
|
|
'@while(%%)' => '<?php while(\1): ?>',
|
238
|
|
|
'@endwhile' => '<?php endwhile; ?>',
|
239
|
|
|
'@continue' => '<?php continue; ?>',
|
240
|
|
|
'@break' => '<?php break; ?>',
|
241
|
|
|
|
242
|
|
|
// with spaces
|
243
|
|
|
'@foreach (%%)' => '<?php foreach(\1): ?>',
|
244
|
|
|
'@for (%%)' => '<?php for(\1): ?>',
|
245
|
|
|
'@while (%%)' => '<?php while(\1): ?>',
|
246
|
|
|
];
|
247
|
|
|
|
248
|
|
|
// php function codes
|
249
|
|
|
$functionsCodes = [
|
250
|
|
|
'@lang(%%)' => '<?php echo $language->getLine(\1); ?>',
|
251
|
|
|
'@include(%%)' => '<?php echo $this->parseFile(\1); ?>',
|
252
|
|
|
'@include(%%, %%)' => '<?php echo $this->parseFile(\1, \2); ?>',
|
253
|
|
|
'@yield(%%)' => '<?php echo $this->sections[\1]; ?>',
|
254
|
|
|
'@each(%%, %%, %%)' => '<?php echo $this->parsePartials(\1, \2, \3); ?>',
|
255
|
|
|
'@extends(%%)' => '@extends not supported',
|
256
|
|
|
'@choice(%%,%%)' => '@choice not supported',
|
257
|
|
|
|
258
|
|
|
// with spaces
|
259
|
|
|
'@lang (%%)' => '<?php echo $language->getLine(\1); ?>',
|
260
|
|
|
'@include (%%)' => '<?php echo $this->parseFile(\1); ?>',
|
261
|
|
|
'@include (%%, %%)' => '<?php echo $this->parseFile(\1, \2); ?>',
|
262
|
|
|
'@yield (%%)' => '<?php echo $this->sections[\1]; ?>',
|
263
|
|
|
'@each (%%, %%, %%)' => '<?php echo $this->parsePartials(\1, \2, \3); ?>',
|
264
|
|
|
'@extends (%%)' => '@extends not supported',
|
265
|
|
|
'@choice (%%,%%)' => '@choice not supported',
|
266
|
|
|
'@lang( %% )' => '<?php echo $language->getLine(\1); ?>',
|
267
|
|
|
'@include( %% )' => '<?php echo $this->parseFile(\1); ?>',
|
268
|
|
|
'@include( %%, %% )' => '<?php echo $this->parseFile(\1, \2); ?>',
|
269
|
|
|
'@yield( %% )' => '<?php echo $this->sections[\1]; ?>',
|
270
|
|
|
'@each( %%, %%, %% )' => '<?php echo $this->parsePartials(\1, \2, \3); ?>',
|
271
|
|
|
'@extends( %% )' => '@extends not supported',
|
272
|
|
|
'@choice( %%,%% )' => '@choice not supported',
|
273
|
|
|
];
|
274
|
|
|
|
275
|
|
|
if ($this->config[ 'allowPhpFunctions' ] === false) {
|
276
|
|
|
$functionsCodes[ '@%%(%%)' ] = '';
|
277
|
|
|
} elseif (is_array($this->config[ 'allowPhpFunctions' ]) AND count(
|
278
|
|
|
$this->config[ 'allowPhpFunctions' ]
|
279
|
|
|
) > 0
|
280
|
|
|
) {
|
281
|
|
|
foreach ($this->config[ 'allowPhpFunctions' ] as $functionName) {
|
282
|
|
|
$functionsCodes[ '@' . $functionName . '(%%)' ] = '<?php echo ' . $functionName . '(\1); ?>';
|
283
|
|
|
}
|
284
|
|
|
}
|
285
|
|
|
|
286
|
|
|
// php variables codes
|
287
|
|
|
$variablesCodes = [
|
288
|
|
|
'{{%% ? %% : %%}}' => '<?php echo (\1 ? \2 : \3); ?>',
|
289
|
|
|
'{{%% or %%}}' => '<?php echo ( empty(\1) ? \2 : \1 ); ?>',
|
290
|
|
|
'{{%% || %%}}' => '<?php echo ( empty(\1) ? \2 : \1 ); ?>',
|
291
|
|
|
'{{$%%->%%(%%)}}' => '<?php echo $\1->\2(\3); ?>',
|
292
|
|
|
'{{$%%->%%}}' => '<?php echo @$\1->\2; ?>',
|
293
|
|
|
'{{$%%[%%]}}' => '<?php echo @$\1[\2]; ?>',
|
294
|
|
|
'{{$%%.%%}}' => '<?php echo @$\1[\2]; ?>',
|
295
|
|
|
'{{$%% = %%}}' => '<?php $\1 = \2; ?>',
|
296
|
|
|
'{{$%%++}}' => '<?php $\1++; ?>',
|
297
|
|
|
'{{$%%--}}' => '<?php $\1--; ?>',
|
298
|
|
|
'{{$%%}}' => '<?php echo (!empty($\1) ? $\1 : ""); ?>',
|
299
|
|
|
'{{/*}}' => '<?php /*',
|
300
|
|
|
'{{*/}}' => '*/ ?>',
|
301
|
|
|
'{{%%}}' => '<?php echo (\1); ?>',
|
302
|
|
|
'{{!! $%% !!}}' => '<?php echo htmlentities($\1, ENT_HTML5); ?>',
|
303
|
|
|
'{{-- %% --}}' => '',
|
304
|
|
|
|
305
|
|
|
// with spaces
|
306
|
|
|
'{{ %% ? %% : %% }}' => '<?php echo (\1 ? \2 : \3); ?>',
|
307
|
|
|
'{{ %% or %% }}' => '<?php echo ( empty(\1) ? \2 : \1 ); ?>',
|
308
|
|
|
'{{ %% || %% }}' => '<?php echo ( empty(\1) ? \2 : \1 ); ?>',
|
309
|
|
|
'{{ $%%->%%(%%) }}' => '<?php echo $\1->\2(\3); ?>',
|
310
|
|
|
'{{ $%%->%% }}' => '<?php echo @$\1->\2; ?>',
|
311
|
|
|
'{{ $%%[%%] }}' => '<?php echo @$\1[\2]; ?>',
|
312
|
|
|
'{{ $%%.%% }}' => '<?php echo @$\1[\2]; ?>',
|
313
|
|
|
'{{ $%% = %% }}' => '<?php $\1 = \2; ?>',
|
314
|
|
|
'{{ $%%++ }}' => '<?php $\1++; ?>',
|
315
|
|
|
'{{ $%%-- }}' => '<?php $\1--; ?>',
|
316
|
|
|
'{{ $%% }}' => '<?php echo (!empty($\1) ? $\1 : ""); ?>',
|
317
|
|
|
'{{ /* }}' => '<?php /*',
|
318
|
|
|
'{{ */ }}' => '*/ ?>',
|
319
|
|
|
'{{ %% }}' => '<?php echo (\1); ?>',
|
320
|
|
|
];
|
321
|
|
|
|
322
|
|
|
if ($this->config[ 'allowPhpConstants' ] === true) {
|
323
|
|
|
$constantsVariables = get_defined_constants(true);
|
324
|
|
|
|
325
|
|
|
if ( ! empty($constantsVariables[ 'user' ])) {
|
326
|
|
|
foreach ($constantsVariables[ 'user' ] as $constant => $value) {
|
327
|
|
|
$variablesCodes[ '{{ ' . $constant . ' }}' ] = '<?php echo ' . $constant . '; ?>';
|
328
|
|
|
}
|
329
|
|
|
}
|
330
|
|
|
}
|
331
|
|
|
|
332
|
|
|
$phpCodes = array_merge($logicalCodes, $loopCodes, $functionsCodes, $variablesCodes);
|
333
|
|
|
|
334
|
|
|
$patterns = $replace = [];
|
335
|
|
|
foreach ($phpCodes as $tplCode => $phpCode) {
|
336
|
|
|
$patterns[] = '#' . str_replace('%%', '(.+)', preg_quote($tplCode, '#')) . '#U';
|
337
|
|
|
$replace[] = $phpCode;
|
338
|
|
|
}
|
339
|
|
|
|
340
|
|
|
/*replace our pseudo language in template with php code*/
|
341
|
|
|
$string = preg_replace($patterns, $replace, $string);
|
342
|
|
|
|
343
|
|
|
extract($this->vars);
|
344
|
|
|
|
345
|
|
|
/*
|
346
|
|
|
* Buffer the output
|
347
|
|
|
*
|
348
|
|
|
* We buffer the output for two reasons:
|
349
|
|
|
* 1. Speed. You get a significant speed boost.
|
350
|
|
|
* 2. So that the final rendered template can be post-processed by
|
351
|
|
|
* the output class. Why do we need post processing? For one thing,
|
352
|
|
|
* in order to show the elapsed page load time. Unless we can
|
353
|
|
|
* intercept the content right before it's sent to the browser and
|
354
|
|
|
* then stop the timer it won't be accurate.
|
355
|
|
|
*/
|
356
|
|
|
ob_start();
|
357
|
|
|
|
358
|
|
|
echo eval('?>' . preg_replace('/;*\s*\?>/', '; ?>', $string));
|
|
|
|
|
359
|
|
|
|
360
|
|
|
$output = ob_get_contents();
|
361
|
|
|
@ob_end_clean();
|
|
|
|
|
362
|
|
|
|
363
|
|
|
return $output;
|
364
|
|
|
}
|
365
|
|
|
|
366
|
|
|
// ------------------------------------------------------------------------
|
367
|
|
|
|
368
|
|
|
/**
|
369
|
|
|
* Blade::collectSection
|
370
|
|
|
*
|
371
|
|
|
* @param string $match
|
372
|
|
|
*
|
373
|
|
|
* @return string
|
374
|
|
|
*/
|
375
|
|
|
private function collectSection($match)
|
376
|
|
|
{
|
377
|
|
|
$section = str_replace(['\'', '(', ')'], '', trim($match[ 1 ]));
|
378
|
|
|
$xSection = explode(',', $section);
|
379
|
|
|
$xSection = array_map('trim', $xSection);
|
380
|
|
|
|
381
|
|
|
$this->sections[ $xSection[ 0 ] ] = $this->replaceString($xSection[ 1 ]);
|
382
|
|
|
|
383
|
|
|
return '';
|
384
|
|
|
}
|
385
|
|
|
|
386
|
|
|
// ------------------------------------------------------------------------
|
387
|
|
|
|
388
|
|
|
/**
|
389
|
|
|
* Blade::collectSectionWithShow
|
390
|
|
|
*
|
391
|
|
|
* @param string $match
|
392
|
|
|
*
|
393
|
|
|
* @return string
|
394
|
|
|
*/
|
395
|
|
|
private function collectSectionWithShow($match)
|
396
|
|
|
{
|
397
|
|
|
$offset = str_replace(['(\'', '\')'], '', trim($match[ 1 ]));
|
398
|
|
|
$this->sections[ $offset ] = $this->replaceString($match[ 2 ]);
|
399
|
|
|
|
400
|
|
|
return '@yield(\'' . $offset . '\')';
|
401
|
|
|
}
|
402
|
|
|
|
403
|
|
|
// ------------------------------------------------------------------------
|
404
|
|
|
|
405
|
|
|
/**
|
406
|
|
|
* Blade::collectSectionWithEnd
|
407
|
|
|
*
|
408
|
|
|
* @param string $match
|
409
|
|
|
*
|
410
|
|
|
* @return string
|
411
|
|
|
*/
|
412
|
|
|
private function collectSectionWithEnd($match)
|
413
|
|
|
{
|
414
|
|
|
$offset = str_replace(['(\'', '\')'], '', trim($match[ 1 ]));
|
415
|
|
|
$content = trim($match[ 2 ]);
|
416
|
|
|
|
417
|
|
|
$this->sections[ $offset ] = $this->replaceString($content);
|
418
|
|
|
|
419
|
|
|
return '';
|
420
|
|
|
}
|
421
|
|
|
|
422
|
|
|
// ------------------------------------------------------------------------
|
423
|
|
|
|
424
|
|
|
/**
|
425
|
|
|
* Blade::collectSectionWithParent
|
426
|
|
|
*
|
427
|
|
|
* @param string $match
|
428
|
|
|
*
|
429
|
|
|
* @return string
|
430
|
|
|
*/
|
431
|
|
|
private function collectSectionWithParent($match)
|
432
|
|
|
{
|
433
|
|
|
$offset = str_replace(['(\'', '\')'], '', trim($match[ 1 ]));
|
434
|
|
|
$content = $this->replaceString($match[ 2 ]);
|
435
|
|
|
|
436
|
|
|
if (isset($this->sections[ $offset ])) {
|
437
|
|
|
$this->sections[ $offset ] .= PHP_EOL . $content;
|
438
|
|
|
} else {
|
439
|
|
|
$this->sections[ $offset ] = $content;
|
440
|
|
|
}
|
441
|
|
|
|
442
|
|
|
return '';
|
443
|
|
|
}
|
444
|
|
|
} |