Overview
Packages
Classes
1: <?php
2:
3: /**
4: * Onion Framework - Spracovanie šablón
5: *
6: * Copyright (c) 2011 Jano Gašpar (http://webstranky.net)
7: *
8: * @author Jano Gašpar
9: * @copyright Copyright (c) 2011 Jano Gašpar
10: * @package Onion::UI
11: *
12: *
13: * {include file}, {include #block}
14: * {block name}
15: * {$variable}, {!$variable}, {$variable['key']}
16: * {if $abc === TRUE}{elseif $def === NULL}{else}
17: * {loop $array}{else}{/loop}
18: * {$variable|filter1:argument1,argument2|filter2:argument1,argument2}
19: *
20: */
21:
22: class Template
23: {
24: /**
25: * @var array úložisko dát pre spracovanie v šablóne
26: */
27: protected $data;
28:
29:
30: /**
31: * @var string adresár kde je uložená aktuálna šablóna
32: */
33: protected $templates_dir;
34:
35:
36: /**
37: * @var string súborova prípona aktuálnej šablóny
38: */
39: protected $extension;
40:
41:
42: /**
43: * @var string cesta k súboru šablóny
44: */
45: protected $template;
46:
47:
48: /**
49: * @var array úložisko blokov, asociatívne pole názov => obsah bloku
50: */
51: protected $blocks = array();
52:
53:
54: /**
55: * @var array úložisko užívateľských makier, asociatívne pole názov => kód (text, php kód, funkcia(pole))
56: */
57: protected $macros = array();
58:
59:
60: /**
61: * @var array úložisko užívateľských filtrov
62: */
63: protected $filters = array();
64:
65:
66: /**
67: * Konštruktor
68: *
69: * @param string cesta k súboru šablóny
70: * @return object
71: */
72: function __construct($template)
73: {
74: $this->data = array();
75:
76: return $this->set_template($template);
77: }
78:
79:
80: /**
81: * Nastavenie súboru k šablóne, ak súbor neexistuje použije sa @default
82: *
83: * @param string cesta k súboru šablóny
84: * @return object
85: */
86: public function set_template($template)
87: {
88: $this->template = $template;
89:
90: $path_parts = pathinfo($template);
91: $this->templates_dir = $path_parts['dirname'];
92: $this->extension = $path_parts['extension'];
93:
94: if (file_exists($template) === FALSE) {
95: $this->template = $this->templates_dir . '/Default.' . $this->extension;
96: }
97:
98: return $this;
99: }
100:
101:
102: /**
103: * Vymazanie úložiska dát
104: *
105: * @return object
106: */
107: public function reset_data()
108: {
109: $this->data = array();
110:
111: return $this;
112: }
113:
114:
115: /**
116: * Vloženie dát do úložiska dát
117: *
118: * @param string|array názov premennej, alebo asociatívne pole s dátami
119: * @param string|array hodnota premennej
120: * @return object
121: */
122: public function set_data($variable, $value = NULL)
123: {
124: if (is_array($variable) === TRUE
125: OR is_object($variable) === TRUE) {
126:
127: $this->data = array_merge($this->data, (array) $variable);
128:
129: } else {
130: $this->data[$variable] = $value;
131: }
132:
133: return $this;
134: }
135:
136:
137: /**
138: * Preklad šablóny do PHP kódu
139: *
140: * @return string preložený kód
141: */
142: public function compile()
143: {
144: $template_name = pathinfo($this->template, PATHINFO_FILENAME);
145:
146: $cache_file = CACHE_DIR . '/templates/' . $template_name . '.php';
147:
148: $template_time = filemtime($this->template);
149:
150: $cache_time = 0;
151: if (file_exists($cache_file) === TRUE) {
152: $cache_time = filemtime($cache_file);
153: }
154:
155: if ($cache_time !== FALSE
156: AND $cache_time > $template_time
157: AND DEBUG === FALSE) {
158:
159: return $cache_file;
160: }
161:
162: $template = file_get_contents($this->template);
163:
164: $template_len = 0;
165:
166: while ($template_len <> strlen($template)) {
167: $template_len = strlen($template);
168:
169: $template = $this->macro_layout($template);
170: $template = $this->macro_block($template);
171: $template = $this->macro_include($template);
172: $template = $this->macro_control_structure($template);
173: $template = $this->macro_widget($template);
174: $template = $this->macro_php($template);
175: }
176:
177: $macros_regexp = array(
178: '{loop +.*?}',
179: '{/loop}',
180: '{!?\$.*?}',
181: );
182:
183: $macros_regexp = '#(' . implode(')|(', $macros_regexp) . ')#ms';
184:
185: $fragments = preg_split($macros_regexp, $template, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
186:
187: $template = '';
188:
189: $loop_level = 0;
190:
191: foreach ($fragments as $fragment) {
192: if (preg_match('#{loop +(\$.*?)}#', $fragment, $match) == TRUE) {
193: $loop_level++;
194:
195: $counter = '$counter_' . $loop_level;
196: $key = '$key_' . $loop_level;
197: $value = '$value_' . $loop_level;
198: $last = '$last_' . $loop_level;
199:
200: $template .= '<?php ' . $counter . ' = -1;';
201: $template .= 'if (empty(' . $match[1] . ') === FALSE)';
202: $template .= $last . ' = (count(' . $match[1] . ') - 1);' .chr(10);
203: $template .= 'if (empty(' . $match[1] . ') === FALSE)';
204: $template .= 'foreach(' . $match[1] . ' as ' . $key . ' => ' . $value . ') {';
205: $template .= $counter . '++;';
206: $template .= '?>';
207:
208: } elseif (preg_match('#{/loop}#', $fragment, $match) == TRUE) {
209: $loop_level--;
210:
211: $template .= '<?php } ?>';
212:
213: } elseif (preg_match('#{!?\$.*?}#', $fragment, $match) == TRUE) {
214: if ($loop_level > 0) {
215: $fragment = preg_replace('#(\$value)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
216: $fragment = preg_replace('#(\$key)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
217: $fragment = preg_replace('#(\$counter)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
218: $fragment = preg_replace('#(\$last)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
219: }
220:
221: $template .= $this->macro_variable($fragment, $loop_level);
222:
223: } else {
224: if ($loop_level > 0) {
225: $fragment = preg_replace('#(\$value)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
226: $fragment = preg_replace('#(\$key)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
227: $fragment = preg_replace('#(\$counter)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
228: $fragment = preg_replace('#(\$last)([ =<>!\)\[]+)#', '$1_' . $loop_level . '$2', $fragment);
229: }
230:
231: $template .= $fragment;
232: }
233: }
234:
235: foreach ($this->macros as $macro => $callback) {
236: $template = $this->macro_custom($template, $macro, $callback);
237: }
238:
239: file_put_contents($cache_file, $template);
240:
241: return $cache_file;
242: }
243:
244:
245: /**
246: * Vykonanie šablóny
247: *
248: * @return string html kód
249: */
250: public function render() {
251: extract($this->data);
252: ob_start();
253: include($this->compile());
254: $contents = ob_get_contents();
255: ob_end_clean();
256: return $contents;
257: }
258:
259:
260: /**
261: * Preklad makra "layout"
262: *
263: * @param string kód šablóny na spracovanie
264: * @return string spracovaný kód šablóny
265: */
266: private function macro_layout($template)
267: {
268: preg_match_all('|{extends +(.*?)}|', $template, $extends);
269:
270: $extends = array_unique($extends[1]);
271:
272: foreach ($extends as $extend) {
273: $include_file = $this->templates_dir . '/' . $extend . '.' . $this->extension;
274: $include_content = file_get_contents($include_file);
275:
276: $include_content = $this->macro_block($include_content);
277:
278: $template = preg_replace('|{extends +'.$extend.'}|', $include_content, $template);
279: }
280:
281: if (empty($extends) === FALSE) {
282: $template = $this->macro_block($template, FALSE);
283: }
284:
285: return $template;
286: }
287:
288:
289: /**
290: * Preklad makra "block"
291: *
292: * @param string kód šablóny na spracovanie
293: * @return string spracovaný kód šablóny
294: */
295: private function macro_block($template, $includes = TRUE)
296: {
297: $pattern = '|{block +(.*?)}(.*?){/block}|sm';
298: preg_match_all($pattern, $template, $blocks);
299:
300: if (empty($blocks) === TRUE) {
301: return $template;
302: }
303:
304: foreach ($blocks[1] as $key => $block_name) {
305: $this->blocks[$block_name] = $blocks[2][$key];
306:
307: if ($includes === TRUE) {
308: $template = str_replace($blocks[0][$key], '{include #' . $block_name . '}', $template);
309:
310: } else {
311: $template = str_replace($blocks[0][$key], '', $template);
312: }
313: }
314:
315: return $template;
316: }
317:
318:
319: /**
320: * Preklad makra "include"
321: *
322: * @param string kód šablóny na spracovanie
323: * @return string spracovaný kód šablóny
324: */
325: private function macro_include($template)
326: {
327: preg_match_all('|{include +(.*?)}|', $template, $includes);
328:
329: $includes = array_unique($includes[1]);
330:
331: foreach ($includes as $include) {
332: if ($include[0] === '#') {
333: $block = substr($include, 1);
334:
335: if (isset($this->blocks[$block]) === TRUE) {
336: $include_content = $this->blocks[$block];
337:
338: } else {
339: $include_content = $block;
340: }
341:
342: } else {
343: $include_file = $this->templates_dir . '/' . $include . '.' . $this->extension;
344: $include_content = file_get_contents($include_file);
345: }
346:
347: $template = preg_replace('|{include +'.$include.'}|', $include_content, $template);
348: }
349:
350: return $template;
351: }
352:
353:
354: /**
355: * Preklad makier "if", "elseif", "else!
356: *
357: * @param string kód šablóny na spracovanie
358: * @return string spracovaný kód šablóny
359: */
360: private function macro_control_structure($template)
361: {
362: $pattern = '|\{if +(.*?)\}|';
363: $replace = '<?php if($1) { ?>';
364: $template = preg_replace($pattern, $replace, $template);
365:
366: $pattern = '|\{elseif +(.*?)\}|';
367: $replace = '<?php } elseif($1) { ?>';
368: $template = preg_replace($pattern, $replace, $template);
369:
370: $template = str_replace('{else}', '<?php } else { ?>', $template);
371:
372: $template = str_replace('{/if}', '<?php } ?>', $template);
373:
374: return $template;
375: }
376:
377:
378: /**
379: * Preklad makra "$"
380: *
381: * @param string kód šablóny na spracovanie
382: * @return string spracovaný kód šablóny
383: */
384: private function macro_variable($variable, $loop_level = NULL)
385: {
386: $variable = substr($variable, 1, -1);
387:
388: $function = NULL;
389: $arguments = NULL;
390:
391: $filters = array();
392: if (strpos($variable, '|') !== FALSE) {
393: $filters = explode('|', $variable);
394:
395: $variable = array_shift($filters);
396: }
397:
398: $escape = TRUE;
399: if ($variable[0] === '!') {
400: $escape = FALSE;
401: $variable = substr($variable, 1);
402: }
403:
404: if ($loop_level == FALSE
405: OR in_array($variable, array('$key', '$counter', '$value', '$last')) === FALSE) {
406:
407: $loop_level = '';
408:
409: } else {
410: $loop_level = '_' . $loop_level;
411: }
412:
413: $php_code = '<?php ';
414:
415: $php_code .= 'if (isset(' . $variable . $loop_level . ') === FALSE) ' . $variable . $loop_level . ' = NULL;';
416: $php_code .= '$__tpl_var__' . $loop_level . ' = ' . $variable . $loop_level . ';';
417:
418: foreach ($filters as $filter) {
419: $args = '';
420: if (strpos($filter, ':') == TRUE) {
421: list($filter, $args) = explode(':', $filter);
422: $args = ', ' . $args;
423: }
424:
425: if (isset($this->filters[$filter]) === TRUE) {
426: $filter = $this->filters[$filter];
427: }
428:
429: if (is_array($filter) === FALSE) {
430: if (strpos($args, '###') == TRUE) {
431: $args = str_replace('###', '$__tpl_var__' . $loop_level, $args);
432: $function = $filter . '(' . trim($args, ',') . ');';
433:
434: } else {
435: $function = $filter . '($__tpl_var__' . $loop_level . $args . ');';
436: }
437:
438: } else {
439: $function = 'call_user_func(array(\'' . $filter[0] .'\', \'' . $filter[1] . '\'), $__tpl_var__' . $loop_level . $args . ');';
440: }
441:
442: $php_code .= '$__tpl_var__' . $loop_level . ' = ' . $function;
443: }
444:
445: if ($escape === TRUE) {
446: $php_code .= 'if (is_object($__tpl_var__' . $loop_level . ') === FALSE) {';
447: $php_code .= 'echo htmlspecialchars($__tpl_var__' . $loop_level . ', ENT_QUOTES);';
448: $php_code .= '} else {';
449: $php_code .= 'echo $__tpl_var__' . $loop_level . ';';
450: $php_code .= '}';
451:
452: } else {
453: $php_code .= 'echo $__tpl_var__' . $loop_level . ';';
454: }
455:
456: $php_code .= '?>';
457:
458: return $php_code;
459: }
460:
461:
462: /**
463: * Preklad makra "widget"
464: *
465: * @param string kód šablóny na spracovanie
466: * @return string spracovaný kód šablóny
467: */
468: private function macro_widget($template)
469: {
470: preg_match_all('|{(!?)widget +(.*?)}|', $template, $widgets);
471:
472: $flags = $widgets[1];
473: $widgets = $widgets[2];
474:
475: foreach ($widgets as $key => $widget) {
476: $segments = explode('_', $widget);
477: $widget_name = '';
478: foreach ($segments as $segment) {
479: $widget_name .= ucfirst($segment);
480: }
481: $widget_name .= 'Widget';
482:
483: $php_code = '<?php ';
484: $php_code .= '$__tpl_object = new ' . $widget_name . '($app);';
485: $php_code .= '$__tpl_object->render(get_defined_vars());';
486: $php_code .= 'if (empty($__tpl_object->data) === FALSE) {extract($__tpl_object->data);';
487: $php_code .= '?>';
488:
489: $include = APP_DIR . '/Widgets/' . $widget_name . '.html';
490: if (file_exists($include) === TRUE) {
491: $php_code .= file_get_contents($include);
492:
493: } else {
494: $var_name = Strings::split_by_upper_chars($widget);
495: $var_name = implode('_', $var_name);
496: $php_code .= '{' . $flags[$key] . '$' . strtolower($var_name) . '}';
497: }
498:
499: $php_code .= '<?php } ?>';
500:
501: $template = preg_replace('|{' . $flags[$key] . 'widget +'.$widget.'}|', $php_code, $template);
502: }
503:
504: return $template;
505: }
506:
507:
508: private function macro_php($template)
509: {
510: $regexp = '|{{(!?\$[a-zA-Z0-9_]+?->[a-zA-Z0-9_]+?\(.*?\))}}|ms';
511:
512: preg_match_all($regexp, $template, $matches);
513:
514: foreach ($matches[1] as $key => $method) {
515: $php_code = '<?php ' . $method . ' ?>';
516:
517: $template = str_replace($matches[0][$key], $php_code, $template);
518: }
519:
520: return $template;
521: }
522:
523:
524: /**
525: * Preklad užívateľských makier
526: *
527: * @param string kód šablóny na spracovanie
528: * @param string makro
529: * @param string|array kód makra, alebo funkcia makra (text, php kód, funkcia(pole))
530: * @return string spracovaný kód šablóny
531: */
532: private function macro_custom($template, $macro, $callback)
533: {
534: $regexp = '|{' . preg_quote($macro) . '(.*?)}|ms';
535:
536: preg_match_all($regexp, $template, $matches);
537:
538: $matches = array_unique($matches[1]);
539:
540: foreach ($matches as $match) {
541: if (is_array($callback) === TRUE) {
542: if (count($callback) === 1) {
543: $callback = $callback[0];
544: }
545:
546: $replacement = call_user_func($callback, $match);
547:
548: } else {
549: $replacement = $callback;
550: }
551:
552: $template = str_replace('{' . $macro . $match . '}', $replacement, $template);
553: }
554:
555: return $template;
556: }
557:
558:
559: /**
560: * Registrácia užívateľkého makra
561: *
562: * @param string makro
563: * @param string|array kód makra, alebo funkcia makra (text, php kód, funkcia(pole))
564: * @return void
565: */
566: public function register_macro($macro, $callback)
567: {
568: $this->macros[$macro] = $callback;
569: }
570:
571: /**
572: * Registrácia užívateľkého filtra
573: *
574: * @param string filter
575: * @param string|array funkcia filtra (názov funkcie, alebo pole array(Trieda, metóda))
576: * @return
577: */
578: public function register_filter($filter, $callback)
579: {
580: $this->filters[$filter] = $callback;
581: }
582: }