-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathThpViewRenderer.php
346 lines (303 loc) · 10.5 KB
/
ThpViewRenderer.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
<?php
/*
* $Id: $
* $Rev: $
* $Date: $
* $Author: $
*/
/**
* ThpViewRenderer class file.
*
* @author Krivtsov Artur (wartur) <[email protected]> | Made in Russia
* @copyright Krivtsov Artur © 2010-2012
* @link https://github.com/wartur/yii-thplike
* @license New BSD License
*/
/**
* Компилятор thp синтаксиса, основанный на CViewRenderer
* Позволяет удобно верстать приложения, делающий валидный html код в View
* Обратно совместимый с PHP, позволяет компилировать THP-Like php код
*
* @author Krivtsov Artur (wartur) <[email protected]> | Made in Russia
* @version 1.4
*/
class ThpViewRenderer extends CViewRenderer
{
// Паттерны вырезания заголовков
const PATTERN_TRUNC_BEGIN = '<!--// BEGIN -->'; // Начало, до которого отрезать
const PATTERN_TRUNC_END = '<!--\\\\ END -->'; // Конец, после которого отрезать
//
// Паттерны замены thp-like
const PATTERN_THPLIKECODE_BEGIN = '<!--<?';
const PATTERN_THPLIKECODE_END = '?>-->';
const PATTERN_THPVAR_BEGIN = '{';
const PATTERN_THPVAR_END = '}';
//
// Строки замены
const REPLACE_THPLIKECODE_BEGIN = '<?php';
const REPLACE_THPLIKECODE_END = '?>';
const REPLACE_THPVAR_BEGIN = '<?=$';
const REPLACE_THPVAR_END = ';?>';
//
// Паттерн выделения php кода, для защиты от замены фигурных скобок внутри php, script, style, (onClock, onEtc..)
const PATTERN_PREG_EXCEPTION = '#<\?(.+?)\?>|<style(.+?)</style>|<script(.+?)</script>|on(\w+)="(.+?)"#s';
//
// Паттерн выделения переменных thp кода
const PATTERN_PREG_THPVAR = '#\{(.+?)\}#s';
//
// Паттерн выделения блоков thp кода
const PATTERN_PREG_THPBLOCK = '#<!--(.+?)-->#s';
//
// Разделитель элементов массива
const ARRAY_DELIMETR = '.';
//
// Разделитель cвойств, методов классов
const PROP_DELIMETR = '->';
//
// Элемент массива по-умолчанию
const ARRAY_DEFAULT_FIRST_ELEMENT = 'e';
/**
* Parses the source view file and saves the results as another file.
* @param string $sourceFile the source view file path
* @param string $viewFile the resulting view file path
*/
protected function generateViewFile($sourceFile, $viewFile)
{
$contents = file_get_contents($sourceFile);
// Отрезать лишнее
$contents = $this->cutBeginEnd($contents);
// Преобразовать Thp-like блоки PHP
$contents = $this->compileThpLikeCode($contents);
// Преобразования THP в PHP
$contents = $this->compileThpCode($contents);
$contents = $this->compileThpVars($contents);
file_put_contents($viewFile, $contents);
}
/**
* Отрезает заголовки шаблонов, если присутсвуют операторы BRGIN, END
*
* @param string $contents Код шаблона
* @return string Код шаблона
*/
private function cutBeginEnd($contents)
{
$beginTruncPos = strpos($contents, self::PATTERN_TRUNC_BEGIN);
if(!empty($beginTruncPos)) // иначе копируем с начала строки
{
$beginTruncPos+= strlen(self::PATTERN_TRUNC_BEGIN);
}
$endTruncPos = strpos($contents, self::PATTERN_TRUNC_END);
if(empty($endTruncPos))
{
// копируем до конца
$result = substr($contents, $beginTruncPos);
}
else
{
// копируем до найденой позиции
$truncLenght = $endTruncPos - $beginTruncPos;
$result = substr($contents, $beginTruncPos, $truncLenght);
}
return $result;
}
/**
* Компилировать код THP
*
* @param string $contents Код шаблона
* @return string Код шаблона
*/
private function compileThpCode($contents)
{
$result = '';
$splitcount = preg_match_all(self::PATTERN_PREG_THPBLOCK, $contents, $splitmatch);
$splitdata = preg_split(self::PATTERN_PREG_THPBLOCK, $contents);
$splitmatch = $splitmatch[1]; // Получим нужный блок
for($i = 0; $i < $splitcount; ++$i)
{
$result.= $splitdata[$i];
$operator = trim($splitmatch[$i]);
$blocktype = substr($operator, 0, 2);
switch($blocktype)
{
case '//':
if(strpos($operator, 'CUT') == 3)
{
for($n = ++$i; $n < $splitcount; ++$n)
{
if(trim($splitmatch[$n]) == '\\\\ CUT')
{
$i = $n;
break;
}
}
}
elseif(strpos($operator, 'LOOP') == 3)
{
$raw_operator = trim(substr($operator, 7));
$prepeare_operator = $this->replacePointToArrayInternal($raw_operator);
$result.= '<?php foreach($'.$prepeare_operator.' as $'.self::ARRAY_DEFAULT_FIRST_ELEMENT.'): ?>';
}
elseif(strpos($operator, 'IFSET') == 3)
{
$raw_operator = trim(substr($operator, 8));
$prepeare_operator = $this->replacePointToArrayInternal($raw_operator);
$result.= '<?php if(isset($'.$prepeare_operator.')): ?>';
}
elseif(strpos($operator, 'IFEMPTY') == 3)
{
$raw_operator = trim(substr($operator, 10));
$prepeare_operator = $this->replacePointToArrayInternal($raw_operator);
$result.= '<?php if(empty($'.$prepeare_operator.')): ?>';
}
elseif(strpos($operator, 'IFTRUE') == 3)
{
$raw_operator = trim(substr($operator, 9));
$prepeare_operator = $this->replacePointToArrayInternal($raw_operator);
$result.= '<?php if($'.$prepeare_operator.'): ?>';
}
else
{
$result.= '<!--'.$operator.'-->';
}
break;
case '\\\\':
if(strpos($operator, 'IF') == 3)
{
$result.= '<?php endif; ?>';
}
elseif(strpos($operator, 'LOOP') == 3)
{
$result.= '<?php endforeach; ?>';
}
else
{
$result.= '<!--'.$operator.'-->';
}
break;
case '||':
if(strpos($operator, 'ELSE') == 3)
{
$result.= '<?php else: ?>';
}
break;
case '!!':
// Вырезение THP комментариев, никаких действий не нужно
break;
default:
$result.= '<!--'.$operator.'-->';
break;
}
}
$result.= $splitdata[$splitcount];
return $result;
}
/**
* Компилирвать переменные THP
* Компилирование происходит исключая код PHP который присутствует
* на странице, то есть исключается возможность замены фигурных скобок
* внутри php
*
* @param string $contents Код шаблона
* @return string Код шаблона
*/
private function compileThpVars($contents)
{
$result = '';
// находим все php блоки, исключаем их из замены
$splitcount = preg_match_all(self::PATTERN_PREG_EXCEPTION, $contents, $splitmatch);
$splitdata = preg_split(self::PATTERN_PREG_EXCEPTION, $contents);
$splitmatch = $splitmatch[0]; // Получим нужный блок
//
// генерация результирующего файла
for($i = 0; $i < $splitcount; ++$i)
{
// Взять и обработать блок
$varcontent = $this->replacePointToArray($splitdata[$i]);
$varcontent = str_replace(self::PATTERN_THPVAR_BEGIN, self::REPLACE_THPVAR_BEGIN, $varcontent);
$varcontent = str_replace(self::PATTERN_THPVAR_END, self::REPLACE_THPVAR_END, $varcontent);
// Записать замененный контент, записать код PHP
$result .= $varcontent;
$result .= $splitmatch[$i];
}
// Обработать последний блок
$varcontent = $this->replacePointToArray($splitdata[$splitcount]);
$varcontent = str_replace(self::PATTERN_THPVAR_BEGIN, self::REPLACE_THPVAR_BEGIN, $varcontent);
$varcontent = str_replace(self::PATTERN_THPVAR_END, self::REPLACE_THPVAR_END, $varcontent);
// Записать замененный контент
$result .= $varcontent;
return $result;
}
/**
* Компилировать THP-like код
* Простая замена одних скобок на другие скобки
*
* @param string $contents Код шаблона
* @return string Код шаблона
*/
private function compileThpLikeCode($contents)
{
$result = str_replace(self::PATTERN_THPLIKECODE_BEGIN, self::REPLACE_THPLIKECODE_BEGIN, $contents);
return str_replace(self::PATTERN_THPLIKECODE_END, self::REPLACE_THPLIKECODE_END, $result);
}
/**
* Производит разбиение кода PHP для выявления блоков за скобками PHP
* Код PHP остается без изменений, код THP отправляется на преобразование
*
* @param string $contents Код шаблона
* @return string Код шаблона
*/
private function replacePointToArray($contents)
{
$result = '';
$splitcount = preg_match_all(self::PATTERN_PREG_THPVAR, $contents, $splitmatch);
$splitdata = preg_split(self::PATTERN_PREG_THPVAR, $contents);
$splitmatch = $splitmatch[1]; // Получим нужный блок
for($i = 0; $i < $splitcount; ++$i)
{
$result_operator = $this->replacePointToArrayInternal($splitmatch[$i]);
$result .= $splitdata[$i];
$result .= '{'.$result_operator.'}';
}
$result .= $splitdata[$splitcount];
return $result;
}
/**
* Преобразование переменной THP в PHP
*
* @param string $contents Код шаблона
* @return string Код шаблона
*/
private function replacePointToArrayInternal($operator)
{
$operator_part = explode(self::ARRAY_DELIMETR, $operator);
$count = count($operator_part);
// Первый элемент оператора
if(empty($operator_part[0]))
$result = self::ARRAY_DEFAULT_FIRST_ELEMENT;
else
{
$strlen_before = strlen($operator_part[0]);
$trim_operator = ltrim($operator_part[0], '$');
$strlen_after = strlen($trim_operator);
if($strlen_before > $strlen_after)
{
$result = 'data->'.$trim_operator;
}
else
{
$result = $operator_part[0];
}
}
for($i = 1; $i < $count; ++$i)
{
$obj_part = explode(self::PROP_DELIMETR, $operator_part[$i]);
$obj_count = count($obj_part);
$result .= "['$obj_part[0]']";
for($n = 1; $n < $obj_count; ++$n)
{
$result .= '->'.$obj_part[$n];
}
}
return $result;
}
}