XpressEngine Core  1.11.2
 All Classes Namespaces Files Functions Variables Pages
TemplateHandler.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) NAVER <http://www.navercorp.com> */
3 
13 {
14 
15  private $compiled_path = 'files/cache/template_compiled/';
16  private $path = NULL;
17  private $filename = NULL;
18  private $file = NULL;
19  private $xe_path = NULL;
20  private $web_path = NULL;
21  private $compiled_file = NULL;
22  private $config = NULL;
23  private $skipTags = NULL;
24  private $handler_mtime = 0;
25  private $autoescape = false;
26  static private $rootTpl = NULL;
27 
32  public function __construct()
33  {
34  ini_set('pcre.jit', "0");
35  $this->xe_path = rtrim(getScriptPath(), '/');
36  $this->compiled_path = _XE_PATH_ . $this->compiled_path;
37  $this->config = new stdClass();
38 
39  $this->ignoreEscape = array(
40  'functions' => function ($m) {
41  $list = array(
42  'htmlspecialchars',
43  'nl2br',
44  );
45  return preg_match('/^(' . implode('|', $list) . ')\(/', $m[1]);
46  },
47  'lang' => function ($m) {
48  // 다국어
49  return preg_match('/^\$lang\->/', trim($m[1]));
50  }
51  );
52 
53  $this->dbinfo = Context::getDBInfo();
54  }
55 
60  static public function &getInstance()
61  {
62  static $oTemplate = NULL;
63 
64  if(__DEBUG__ == 3)
65  {
66  if(!isset($GLOBALS['__TemplateHandlerCalled__']))
67  {
68  $GLOBALS['__TemplateHandlerCalled__'] = 1;
69  }
70  else
71  {
72  $GLOBALS['__TemplateHandlerCalled__']++;
73  }
74  }
75 
76  if(!$oTemplate)
77  {
78  $oTemplate = new TemplateHandler();
79  }
80 
81  return $oTemplate;
82  }
83 
91  protected function init($tpl_path, $tpl_filename, $tpl_file = '')
92  {
93  // verify arguments
94  if(substr($tpl_path, -1) != '/')
95  {
96  $tpl_path .= '/';
97  }
98  if(!is_dir($tpl_path))
99  {
100  return;
101  }
102  if(!file_exists($tpl_path . $tpl_filename) && file_exists($tpl_path . $tpl_filename . '.html'))
103  {
104  $tpl_filename .= '.html';
105  }
106 
107  // create tpl_file variable
108  if(!$tpl_file)
109  {
110  $tpl_file = $tpl_path . $tpl_filename;
111  }
112 
113  // set template file infos.
114  $this->path = $tpl_path;
115  $this->filename = $tpl_filename;
116  $this->file = $tpl_file;
117 
118  $this->web_path = $this->xe_path . '/' . ltrim(preg_replace('@^' . preg_quote(_XE_PATH_, '@') . '|\./@', '', $this->path), '/');
119 
120  // get compiled file name
121  $hash = md5($this->file . __XE_VERSION__);
122  $this->compiled_file = "{$this->compiled_path}{$hash}.compiled.php";
123 
124  $this->autoescape = $this->isAutoescape();
125 
126  // compare various file's modified time for check changed
127  $this->handler_mtime = filemtime(__FILE__);
128 
129  $skip = array('');
130  }
131 
139  public function compile($tpl_path, $tpl_filename, $tpl_file = '')
140  {
141  $buff = false;
142 
143  // store the starting time for debug information
144  if(__DEBUG__ == 3)
145  {
146  $start = getMicroTime();
147  }
148 
149  // initiation
150  $this->init($tpl_path, $tpl_filename, $tpl_file);
151 
152  // if target file does not exist exit
153  if(!$this->file || !file_exists($this->file))
154  {
155  return "Err : '{$this->file}' template file does not exists.";
156  }
157 
158  // for backward compatibility
159  if(is_null(self::$rootTpl))
160  {
161  self::$rootTpl = $this->file;
162  }
163 
164  $source_template_mtime = filemtime($this->file);
165  $latest_mtime = $source_template_mtime > $this->handler_mtime ? $source_template_mtime : $this->handler_mtime;
166 
167  // cache control
168  $oCacheHandler = CacheHandler::getInstance('template');
169 
170  // get cached buff
171  if($oCacheHandler->isSupport())
172  {
173  $cache_key = 'template:' . $this->file;
174  $buff = $oCacheHandler->get($cache_key, $latest_mtime);
175  }
176  else
177  {
178  if(is_readable($this->compiled_file) && filemtime($this->compiled_file) > $latest_mtime && filesize($this->compiled_file))
179  {
180  $buff = 'file://' . $this->compiled_file;
181  }
182  }
183 
184  if($buff === FALSE)
185  {
186  $buff = $this->parse();
187  if($oCacheHandler->isSupport())
188  {
189  $oCacheHandler->put($cache_key, $buff);
190  }
191  else
192  {
193  FileHandler::writeFile($this->compiled_file, $buff);
194  }
195  }
196 
197  $output = $this->_fetch($buff);
198 
199  if($__templatehandler_root_tpl == $this->file)
200  {
201  $__templatehandler_root_tpl = null;
202  }
203 
204  // store the ending time for debug information
205  if(__DEBUG__ == 3)
206  {
207  $GLOBALS['__template_elapsed__'] += getMicroTime() - $start;
208  }
209 
210  return $output;
211  }
212 
219  public function compileDirect($tpl_path, $tpl_filename)
220  {
221  $this->init($tpl_path, $tpl_filename, null);
222 
223  // if target file does not exist exit
224  if(!$this->file || !file_exists($this->file))
225  {
226  Context::close();
227  exit("Cannot find the template file: '{$this->file}'");
228  }
229 
230  return $this->parse();
231  }
232 
238  protected function parse($buff = null)
239  {
240  if(is_null($buff))
241  {
242  if(!is_readable($this->file))
243  {
244  return;
245  }
246 
247  // read tpl file
248  $buff = FileHandler::readFile($this->file);
249  }
250 
251  // HTML tags to skip
252  if(is_null($this->skipTags))
253  {
254  $this->skipTags = array('marquee');
255  }
256 
257  // reset config for this buffer (this step is necessary because we use a singleton for every template)
258  $previous_config = clone $this->config;
259  $this->config = new stdClass();
260  $this->config->autoescape = null;
261 
262  if(preg_match('/<config( [^>\/]+)/', $buff, $config_match))
263  {
264  if(preg_match_all('@ (?<name>\w+)="(?<value>[^"]+)"@', $config_match[1], $config_matches, PREG_SET_ORDER))
265  {
266  foreach($config_matches as $config_match)
267  {
268  if($config_match['name'] === 'autoescape')
269  {
270  $this->config->autoescape = $config_match['value'];
271  }
272  }
273  }
274  }
275 
276  if($this->config->autoescape === 'on') $this->autoescape = true;
277 
278  // replace comments
279  $buff = preg_replace('@<!--//.*?-->@s', '', $buff);
280 
281  // replace value of src in img/input/script tag
282  $buff = preg_replace_callback('/<(?:img|input|script)(?:[^<>]*?)(?(?=cond=")(?:cond="[^"]+"[^<>]*)+|)[^<>]* src="(?!(?:https?|file):\/\/|[\/\{])([^"]+)"/is', array($this, '_replacePath'), $buff);
283 
284  // replace loop and cond template syntax
285  $buff = $this->_parseInline($buff);
286 
287  // include, unload/load, import
288  $buff = preg_replace_callback('/{(@[\s\S]+?|(?=\$\w+|_{1,2}[A-Z]+|[!\(+-]|\w+(?:\(|::)|\d+|[\'"].*?[\'"]).+?)}|<(!--[#%])?(include|import|(un)?load(?(4)|(?:_js_plugin)?)|config)(?(2)\(["\']([^"\']+)["\'])(.*?)(?(2)\)--|\/)>|<!--(@[a-z@]*)([\s\S]*?)-->(\s*)/', array($this, '_parseResource'), $buff);
289 
290  // remove block which is a virtual tag
291  $buff = preg_replace('@</?block\s*>@is', '', $buff);
292 
293  // form auto generation
294  $temp = preg_replace_callback('/(<form(?:<\?php.+?\?>|[^<>]+)*?>)(.*?)(<\/form>)/is', array($this, '_compileFormAuthGeneration'), $buff);
295  if($temp)
296  {
297  $buff = $temp;
298  }
299 
300  // prevent from calling directly before writing into file
301  $buff = '<?php if(!defined("__XE__"))exit;?>' . $buff;
302 
303  // remove php script reopening
304  $buff = preg_replace(array('/(\n|\r\n)+/', '/(;)?( )*\?><\?php([\n\t ]+)?/'), array("\n", ";\n"), $buff);
305 
306  // restore config to previous value
307  $this->config = $previous_config;
308 
309  return $buff;
310  }
311 
321  private function _compileFormAuthGeneration($matches)
322  {
323  // form ruleset attribute move to hidden tag
324  if($matches[1])
325  {
326  preg_match('/ruleset="([^"]*?)"/is', $matches[1], $m);
327  if($m[0])
328  {
329  $matches[1] = preg_replace('/' . addcslashes($m[0], '?$') . '/i', '', $matches[1]);
330 
331  if(strpos($m[1], '@') !== FALSE)
332  {
333  $path = str_replace('@', '', $m[1]);
334  $path = './files/ruleset/' . $path . '.xml';
335  }
336  else if(strpos($m[1], '#') !== FALSE)
337  {
338  $fileName = str_replace('#', '', $m[1]);
339  $fileName = str_replace('<?php echo ', '', $fileName);
340  $fileName = str_replace(' ?>', '', $fileName);
341  $path = '#./files/ruleset/' . $fileName . '.xml';
342 
343  preg_match('@(?:^|\.?/)(modules/[\w-]+)@', $this->path, $mm);
344  $module_path = $mm[1];
345  list($rulsetFile) = explode('.', $fileName);
346  $autoPath = $module_path . '/ruleset/' . $rulsetFile . '.xml';
347  $m[1] = $rulsetFile;
348  }
349  else if(preg_match('@(?:^|\.?/)(modules/[\w-]+)@', $this->path, $mm))
350  {
351  $module_path = $mm[1];
352  $path = $module_path . '/ruleset/' . $m[1] . '.xml';
353  }
354 
355  $matches[2] = '<input type="hidden" name="ruleset" value="' . $m[1] . '" />' . $matches[2];
356  //assign to addJsFile method for js dynamic recache
357  $matches[1] = '<?php Context::addJsFile("' . $path . '", FALSE, "", 0, "body", TRUE, "' . $autoPath . '") ?' . '>' . $matches[1];
358  }
359  }
360 
361  // if not exists default hidden tag, generate hidden tag
362  preg_match_all('/<input[^>]* name="(act|mid|vid)"/is', $matches[2], $m2);
363  $checkVar = array('act', 'mid', 'vid');
364  $resultArray = array_diff($checkVar, $m2[1]);
365  if(is_array($resultArray))
366  {
367  $generatedHidden = '';
368  foreach($resultArray AS $key => $value)
369  {
370  $generatedHidden .= '<input type="hidden" name="' . $value . '" value="<?php echo $__Context->' . $value . ' ?>" />';
371  }
372  $matches[2] = $generatedHidden . $matches[2];
373  }
374 
375  // return url generate
376  if(!preg_match('/no-error-return-url="true"/i', $matches[1]))
377  {
378  preg_match('/<input[^>]*name="error_return_url"[^>]*>/is', $matches[2], $m3);
379  if(!$m3[0])
380  $matches[2] = '<input type="hidden" name="error_return_url" value="<?php echo htmlspecialchars(getRequestUriByServerEnviroment(), ENT_COMPAT | ENT_HTML401, \'UTF-8\', false) ?>" />' . $matches[2];
381  }
382  else
383  {
384  $matches[1] = preg_replace('/no-error-return-url="true"/i', '', $matches[1]);
385  }
386 
387  $matches[0] = '';
388  return implode($matches);
389  }
390 
396  private function _fetch($buff)
397  {
398  if(!$buff)
399  {
400  return;
401  }
402 
403  $__Context = &$GLOBALS['__Context__'];
404  $__Context->tpl_path = $this->path;
405 
406  if($_SESSION['is_logged'])
407  {
408  $__Context->logged_info = Context::get('logged_info');
409  }
410 
411  $level = ob_get_level();
412  ob_start();
413  if(substr($buff, 0, 7) == 'file://')
414  {
415  if(__DEBUG__)
416  {
417  //load cache file from disk
418  $eval_str = FileHandler::readFile(substr($buff, 7));
419  $eval_str_buffed = "?>" . $eval_str;
420  @eval($eval_str_buffed);
421  $error_info = error_get_last();
422  //parse error
423  if ($error_info['type'] == 4)
424  {
425  throw new Exception("Error Parsing Template - {$error_info['message']} in template file {$this->file}");
426  }
427  }
428  else
429  {
430  include(substr($buff, 7));
431  }
432  }
433  else
434  {
435  $eval_str = "?>" . $buff;
436  @eval($eval_str);
437  $error_info = error_get_last();
438  //parse error
439  if ($error_info['type'] == 4)
440  {
441  throw new Exception("Error Parsing Template - {$error_info['message']} in template file {$this->file}");
442  }
443  }
444 
445  $contents = '';
446  while (ob_get_level() - $level > 0) {
447  $contents .= ob_get_contents();
448  ob_end_clean();
449  }
450  return $contents;
451  }
452 
461  private function _replacePath($match)
462  {
463  //return origin conde when src value started '${'.
464  if(preg_match('@^\${@', $match[1]))
465  {
466  return $match[0];
467  }
468 
469  //return origin code when src value include variable.
470  if(preg_match('@^[\'|"]\s*\.\s*\$@', $match[1]))
471  {
472  return $match[0];
473  }
474 
475  $src = preg_replace('@^(\./)+@', '', trim($match[1]));
476 
477  $src = $this->web_path . $src;
478  $src = str_replace('/./', '/', $src);
479 
480  // for backward compatibility
481  $src = preg_replace('@/((?:[\w-]+/)+)\1@', '/\1', $src);
482 
483  while(($tmp = preg_replace('@[^/]+/\.\./@', '', $src, 1)) !== $src)
484  {
485  $src = $tmp;
486  }
487 
488  return substr($match[0], 0, -strlen($match[1]) - 6) . "src=\"{$src}\"";
489  }
490 
496  private function _parseInline($buff)
497  {
498  if(!preg_match_all('/<([a-zA-Z]+\d?)(?:\s)/', $buff, $match))
499  {
500  return $buff;
501  }
502 
503  $tags = array_diff(array_unique($match[1]), $this->skipTags);
504 
505  if(!count($tags))
506  {
507  return $buff;
508  }
509 
510  $tags = '(?:' . implode('|', $tags) . ')';
511  $split_regex = "@(<(?>/?{$tags})(?>[^<>\{\}\"']+|<!--.*?-->|{[^}]+}|\".*?\"|'.*?'|.)*?>)@s";
512 
513  $nodes = preg_split($split_regex, $buff, -1, PREG_SPLIT_DELIM_CAPTURE);
514 
515  // list of self closing tags
516  $self_closing = array('area' => 1, 'base' => 1, 'basefont' => 1, 'br' => 1, 'hr' => 1, 'input' => 1, 'img' => 1, 'link' => 1, 'meta' => 1, 'param' => 1, 'frame' => 1, 'col' => 1);
517 
518  for($idx = 1, $node_len = count($nodes); $idx < $node_len; $idx+=2)
519  {
520  if(!($node = $nodes[$idx]))
521  {
522  continue;
523  }
524 
525  if(preg_match_all('@\s(loop|cond)="([^"]+)"@', $node, $matches))
526  {
527  // this tag
528  $tag = substr($node, 1, strpos($node, ' ') - 1);
529 
530  // if the vale of $closing is 0, it means 'skipping'
531  $closing = 0;
532 
533  // process opening tag
534  foreach($matches[1] as $n => $stmt)
535  {
536  $expr = $matches[2][$n];
537  $expr = $this->_replaceVar($expr);
538  $closing++;
539 
540  switch($stmt)
541  {
542  case 'cond':
543  $nodes[$idx - 1] .= "<?php if({$expr}){ ?>";
544  break;
545  case 'loop':
546  if(!preg_match('@^(?:(.+?)=>(.+?)(?:,(.+?))?|(.*?;.*?;.*?)|(.+?)\s*=\s*(.+?))$@', $expr, $expr_m))
547  {
548  break;
549  }
550  if($expr_m[1])
551  {
552  $expr_m[1] = trim($expr_m[1]);
553  $expr_m[2] = trim($expr_m[2]);
554  if($expr_m[3])
555  {
556  $expr_m[2] .= '=>' . trim($expr_m[3]);
557  }
558  $nodes[$idx - 1] .= "<?php if({$expr_m[1]}&&count({$expr_m[1]}))foreach({$expr_m[1]} as {$expr_m[2]}){ ?>";
559  }
560  elseif($expr_m[4])
561  {
562  $nodes[$idx - 1] .= "<?php for({$expr_m[4]}){ ?>";
563  }
564  elseif($expr_m[5])
565  {
566  $nodes[$idx - 1] .= "<?php while({$expr_m[5]}={$expr_m[6]}){ ?>";
567  }
568  break;
569  }
570  }
571  $node = preg_replace('@\s(loop|cond)="([^"]+)"@', '', $node);
572 
573  // find closing tag
574  $close_php = '<?php ' . str_repeat('}', $closing) . ' ?>';
575  // self closing tag
576  if($node{1} == '!' || substr($node, -2, 1) == '/' || isset($self_closing[$tag]))
577  {
578  $nodes[$idx + 1] = $close_php . $nodes[$idx + 1];
579  }
580  else
581  {
582  $depth = 1;
583  for($i = $idx + 2; $i < $node_len; $i+=2)
584  {
585  $nd = $nodes[$i];
586  if(strpos($nd, $tag) === 1)
587  {
588  $depth++;
589  }
590  elseif(strpos($nd, '/' . $tag) === 1)
591  {
592  $depth--;
593  if(!$depth)
594  {
595  $nodes[$i - 1] .= $nodes[$i] . $close_php;
596  $nodes[$i] = '';
597  break;
598  }
599  }
600  }
601  }
602  }
603 
604  if(strpos($node, '|cond="') !== false)
605  {
606  $node = preg_replace('@(\s[-\w:]+(?:="[^"]+?")?)\|cond="(.+?)"@s', '<?php if($2){ ?>$1<?php } ?>', $node);
607  $node = $this->_replaceVar($node);
608  }
609 
610  if($nodes[$idx] != $node)
611  {
612  $nodes[$idx] = $node;
613  }
614  }
615 
616  $buff = implode('', $nodes);
617 
618  return $buff;
619  }
620 
627  private function _parseResource($m)
628  {
629  $escape_option = 'noescape';
630 
631  if($this->autoescape)
632  {
633  $escape_option = 'autoescape';
634  }
635 
636  // 템플릿에서 명시적으로 off이면 'noescape' 적용
637  if ($this->config->autoescape === 'off') {
638  $escape_option = 'noescape';
639  }
640 
641  // {@ ... } or {$var} or {func(...)}
642  if($m[1])
643  {
644  if(preg_match('@^(\w+)\(@', $m[1], $mm) && !function_exists($mm[1]))
645  {
646  return $m[0];
647  }
648 
649  if($m[1]{0} == '@')
650  {
651  $m[1] = $this->_replaceVar(substr($m[1], 1));
652  return "<?php {$m[1]} ?>";
653  }
654  else
655  {
656  // Get escape options.
657  foreach ($this->ignoreEscape as $key => $value)
658  {
659  if($this->ignoreEscape[$key]($m))
660  {
661  $escape_option = 'noescape';
662  break;
663  }
664  }
665 
666  // Separate filters from variable.
667  if (preg_match('@^(.+?)(?<![|\s])((?:\|[a-z]{2}[a-z0-9_]+(?::.+)?)+)$@', $m[1], $mm))
668  {
669  $m[1] = $mm[1];
670  $filters = array_map('trim', explode_with_escape('|', substr($mm[2], 1)));
671  }
672  else
673  {
674  $filters = array();
675  }
676 
677  // Process the variable.
678  $var = self::_replaceVar($m[1]);
679 
680  // Apply filters.
681  foreach ($filters as $filter)
682  {
683  // Separate filter option from the filter name.
684  if (preg_match('/^([a-z0-9_-]+):(.+)$/', $filter, $matches))
685  {
686  $filter = $matches[1];
687  $filter_option = $matches[2];
688  if (!self::_isVar($filter_option) && !preg_match("/^'.*'$/", $filter_option) && !preg_match('/^".*"$/', $filter_option))
689  {
690  $filter_option = "'" . escape_sqstr($filter_option) . "'";
691  }
692  else
693  {
694  $filter_option = self::_replaceVar($filter_option);
695  }
696  }
697  else
698  {
699  $filter_option = null;
700  }
701 
702  // Apply each filter.
703  switch ($filter)
704  {
705  case 'auto':
706  case 'autoescape':
707  case 'escape':
708  case 'noescape':
709  $escape_option = $filter;
710  break;
711 
712  case 'escapejs':
713  $var = "escape_js({$var})";
714  $escape_option = 'noescape';
715  break;
716 
717  case 'json':
718  $var = "json_encode({$var})";
719  $escape_option = 'noescape';
720  break;
721 
722  case 'strip':
723  case 'strip_tags':
724  $var = $filter_option ? "strip_tags({$var}, {$filter_option})" : "strip_tags({$var})";
725  break;
726 
727  case 'trim':
728  $var = "trim({$var})";
729  break;
730 
731  case 'urlencode':
732  $var = "rawurlencode({$var})";
733  $escape_option = 'noescape';
734  break;
735 
736  case 'lower':
737  $var = "strtolower({$var})";
738  break;
739 
740  case 'upper':
741  $var = "strtoupper({$var})";
742  break;
743 
744  case 'nl2br':
745  $var = $this->_applyEscapeOption($var, $escape_option);
746  $var = "nl2br({$var})";
747  $escape_option = 'noescape';
748  break;
749 
750  case 'join':
751  $var = $filter_option ? "implode({$filter_option}, {$var})" : "implode(', ', {$var})";
752  break;
753 
754  // case 'date':
755  // $var = $filter_option ? "getDisplayDateTime(ztime({$var}), {$filter_option})" : "getDisplayDateTime(ztime({$var}), 'Y-m-d H:i:s')";
756  // $escape_option = 'noescape';
757  // break;
758 
759  case 'format':
760  case 'number_format':
761  $var = $filter_option ? "number_format({$var}, {$filter_option})" : "number_format({$var})";
762  $escape_option = 'noescape';
763  break;
764 
765  case 'link':
766  $var = $this->_applyEscapeOption($var, 'autoescape');
767  if ($filter_option)
768  {
769  $filter_option = $this->_applyEscapeOption($filter_option, 'autoescape');
770  $var = "'<a href=\"' . {$filter_option} . '\">' . {$var} . '</a>'";
771  }
772  else
773  {
774  $var = "'<a href=\"' . {$var} . '\">' . {$var} . '</a>'";
775  }
776  $escape_option = 'noescape';
777  break;
778 
779  default:
780  $filter = escape_sqstr($filter);
781  $var = "'INVALID FILTER ({$filter})'";
782  $escape_option = 'noescape';
783  }
784  }
785 
786  // Apply the escape option and return.
787  return '<?php echo ' . $this->_applyEscapeOption($var, $escape_option) . ' ?>';
788  }
789  }
790 
791  if($m[3])
792  {
793  $attr = array();
794  if($m[5])
795  {
796  if(preg_match_all('@,(\w+)="([^"]+)"@', $m[6], $mm))
797  {
798  foreach($mm[1] as $idx => $name)
799  {
800  $attr[$name] = $mm[2][$idx];
801  }
802  }
803  $attr['target'] = $m[5];
804  }
805  else
806  {
807  if(!preg_match_all('@ (\w+)="([^"]+)"@', $m[6], $mm))
808  {
809  return $m[0];
810  }
811  foreach($mm[1] as $idx => $name)
812  {
813  $attr[$name] = $mm[2][$idx];
814  }
815  }
816 
817  switch($m[3])
818  {
819  // <!--#include--> or <include ..>
820  case 'include':
821  if(!$this->file || !$attr['target'])
822  {
823  return '';
824  }
825 
826  $pathinfo = pathinfo($attr['target']);
827  $fileDir = $this->_getRelativeDir($pathinfo['dirname']);
828 
829  if(!$fileDir)
830  {
831  return '';
832  }
833 
834  return "<?php \$__tpl=TemplateHandler::getInstance();echo \$__tpl->compile('{$fileDir}','{$pathinfo['basename']}') ?>";
835  // <!--%load_js_plugin-->
836  case 'load_js_plugin':
837  $plugin = $this->_replaceVar($m[5]);
838  $s = "<!--#JSPLUGIN:{$plugin}-->";
839  if(strpos($plugin, '$__Context') === false)
840  {
841  $plugin = "'{$plugin}'";
842  }
843 
844  $s .= "<?php Context::loadJavascriptPlugin({$plugin}); ?>";
845  return $s;
846  // <load ...> or <unload ...> or <!--%import ...--> or <!--%unload ...-->
847  case 'import':
848  case 'load':
849  case 'unload':
850  $metafile = '';
851  $pathinfo = pathinfo($attr['target']);
852  $doUnload = ($m[3] === 'unload');
853  $isRemote = !!preg_match('@^(https?:)?//@i', $attr['target']);
854 
855  if(!$isRemote)
856  {
857  if(!preg_match('@^\.?/@', $attr['target']))
858  {
859  $attr['target'] = './' . $attr['target'];
860  }
861  if(substr($attr['target'], -5) == '/lang')
862  {
863  $pathinfo['dirname'] .= '/lang';
864  $pathinfo['basename'] = '';
865  $pathinfo['extension'] = 'xml';
866  }
867 
868  $relativeDir = $this->_getRelativeDir($pathinfo['dirname']);
869 
870  $attr['target'] = $relativeDir . '/' . $pathinfo['basename'];
871  }
872 
873  switch($pathinfo['extension'])
874  {
875  case 'xml':
876  if($isRemote || $doUnload)
877  {
878  return '';
879  }
880  // language file?
881  if($pathinfo['basename'] == 'lang.xml' || substr($pathinfo['dirname'], -5) == '/lang')
882  {
883  $result = "Context::loadLang('{$relativeDir}');";
884  }
885  else
886  {
887  $result = "require_once('./classes/xml/XmlJsFilter.class.php');\$__xmlFilter=new XmlJsFilter('{$relativeDir}','{$pathinfo['basename']}');\$__xmlFilter->compile();";
888  }
889  break;
890  case 'js':
891  if($doUnload)
892  {
893  $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}');";
894  }
895  else
896  {
897  $metafile = $attr['target'];
898  $result = "\$__tmp=array('{$attr['target']}','{$attr['type']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);";
899  }
900  break;
901  case 'css':
902  if($doUnload)
903  {
904  $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');";
905  }
906  else
907  {
908  $metafile = $attr['target'];
909  $result = "\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);";
910  }
911  break;
912  }
913 
914  $result = "<?php {$result} ?>";
915  if($metafile)
916  {
917  $result = "<!--#Meta:{$metafile}-->" . $result;
918  }
919 
920  return $result;
921  // <config ...>
922  case 'config':
923  $result = '';
924  if(preg_match_all('@ (\w+)="([^"]+)"@', $m[6], $config_matches, PREG_SET_ORDER))
925  {
926  foreach($config_matches as $config_match)
927  {
928  $result .= "\$this->config->{$config_match[1]} = '" . trim(strtolower($config_match[2])) . "';";
929  }
930  }
931  return "<?php {$result} ?>";
932  }
933  }
934 
935  // <!--@..--> such as <!--@if($cond)-->, <!--@else-->, <!--@end-->
936  if($m[7])
937  {
938  $m[7] = substr($m[7], 1);
939  if(!$m[7])
940  {
941  return '<?php ' . $this->_replaceVar($m[8]) . '{ ?>' . $m[9];
942  }
943  if(!preg_match('/^(?:((?:end)?(?:if|switch|for(?:each)?|while)|end)|(else(?:if)?)|(break@)?(case|default)|(break))$/', $m[7], $mm))
944  {
945  return '';
946  }
947  if($mm[1])
948  {
949  if($mm[1]{0} == 'e')
950  {
951  return '<?php } ?>' . $m[9];
952  }
953 
954  $precheck = '';
955  if($mm[1] == 'switch')
956  {
957  $m[9] = '';
958  }
959  elseif($mm[1] == 'foreach')
960  {
961  $var = preg_replace('/^\s*\(\s*(.+?) .*$/', '$1', $m[8]);
962  $precheck = "if({$var}&&count({$var}))";
963  }
964  return '<?php ' . $this->_replaceVar($precheck . $m[7] . $m[8]) . '{ ?>' . $m[9];
965  }
966  if($mm[2])
967  {
968  return "<?php }{$m[7]}" . $this->_replaceVar($m[8]) . "{ ?>" . $m[9];
969  }
970  if($mm[4])
971  {
972  return "<?php " . ($mm[3] ? 'break;' : '') . "{$m[7]} " . trim($m[8], '()') . ": ?>" . $m[9];
973  }
974  if($mm[5])
975  {
976  return "<?php break; ?>";
977  }
978  return '';
979  }
980  return $m[0];
981  }
982 
986  private function _applyEscapeOption($str, $escape_option = 'noescape')
987  {
988  switch($escape_option)
989  {
990  case 'escape':
991  return "escape({$str}, true)";
992  case 'noescape':
993  return "{$str}";
994  case 'autoescape':
995  return "escape({$str}, false)";
996  case 'auto':
997  default:
998  return "(\$this->config->autoescape === 'on' ? escape({$str}, false) : ({$str}))";
999  }
1000  }
1001 
1007  function _getRelativeDir($path)
1008  {
1009  $_path = $path;
1010 
1011  $fileDir = strtr(realpath($this->path), '\\', '/');
1012  if($path{0} != '/')
1013  {
1014  $path = strtr(realpath($fileDir . '/' . $path), '\\', '/');
1015  }
1016 
1017  // for backward compatibility
1018  if(!$path)
1019  {
1020  $dirs = explode('/', $fileDir);
1021  $paths = explode('/', $_path);
1022  $idx = array_search($paths[0], $dirs);
1023 
1024  if($idx !== false)
1025  {
1026  while($dirs[$idx] && $dirs[$idx] === $paths[0])
1027  {
1028  array_splice($dirs, $idx, 1);
1029  array_shift($paths);
1030  }
1031  $path = strtr(realpath($fileDir . '/' . implode('/', $paths)), '\\', '/');
1032  }
1033  }
1034 
1035  $path = preg_replace('/^' . preg_quote(_XE_PATH_, '/') . '/', '', $path);
1036 
1037  return $path;
1038  }
1039 
1046  private static function _isVar($str)
1047  {
1048  return preg_match('@(?<!::|\\\\|(?<!eval\()\')\$([a-z_][a-z0-9_]*)@i', $str) ? true : false;
1049  }
1050 
1056  function _replaceVar($php)
1057  {
1058  if(!strlen($php))
1059  {
1060  return '';
1061  }
1062  return preg_replace('@(?<!::|\\\\|(?<!eval\()\')\$([a-z]|_[a-z0-9])@i', '\$__Context->$1', $php);
1063  }
1064 
1065  function isAutoescape()
1066  {
1067  $absPath = str_replace(_XE_PATH_, '', $this->path);
1068  $dirTpl = '(addon|admin|adminlogging|autoinstall|board|comment|communication|counter|document|editor|file|importer|install|integration_search|krzip|layout|member|menu|message|module|page|point|poll|rss|seo|session|spamfilter|syndication|tag|trash|widget)';
1069  $dirSkins = '(layouts\/default|layouts\/user_layout|layouts\/xedition|layouts\/xedition\/demo|m\.layouts\/colorCode|m\.layouts\/default|m\.layouts\/simpleGray|modules\/board\/m\.skins\/default|modules\/board\/m\.skins\/simpleGray|modules\/board\/skins\/default|modules\/board\/skins\/xedition|modules\/communication\/m\.skins\/default|modules\/communication\/skins\/default|modules\/editor\/skins\/ckeditor|modules\/editor\/skins\/xpresseditor|modules\/integration_search\/skins\/default|modules\/layout\/faceoff|modules\/member\/m\.skins\/default|modules\/member\/skins\/default|modules\/message\/m\.skins\/default|modules\/message\/skins\/default|modules\/message\/skins\/xedition|modules\/page\/m\.skins\/default|modules\/page\/skins\/default|modules\/poll\/skins\/default|modules\/poll\/skins\/simple|widgets\/content\/skins\/default|widgets\/counter_status\/skins\/default|widgets\/language_select\/skins\/default|widgets\/login_info\/skins\/default|widgets\/mcontent\/skins\/default|widgetstyles\/simple)';
1070 
1071  // 'tpl'
1072  if(preg_match('/^(\.\/)?(modules\/' . $dirTpl . '|common)\/tpl\//', $absPath))
1073  {
1074  return true;
1075  }
1076 
1077  // skin, layout
1078  if(preg_match('/^(\.\/)?\(' . $dirSkin . '\//', $absPath))
1079  {
1080  return true;
1081  }
1082 
1083  return false;
1084  }
1085 
1086  public function setAutoescape($val = true)
1087  {
1088  $this->autoescape = $val;
1089  }
1090 }
1091 /* End of File: TemplateHandler.class.php */
1092 /* Location: ./classes/template/TemplateHandler.class.php */
explode_with_escape($delimiter, $str, $limit=0, $escape_char= '\\')
Definition: func.inc.php:1881
const __XE_VERSION__
Definition: config.inc.php:32
getMicroTime()
Definition: func.inc.php:986
if(file_exists(_XE_PATH_. 'config/config.user.inc.php')) if(!defined('__DEBUG__')) if(!defined('__DEBUG_OUTPUT__')) if(!defined('__DEBUG_PROTECT__')) if(!defined('__DEBUG_PROTECT_IP__')) if(!defined('__DEBUG_DB_OUTPUT__')) if(!defined('__LOG_SLOW_QUERY__')) if(!defined('__LOG_SLOW_TRIGGER__')) if(!defined('__LOG_SLOW_ADDON__')) if(!defined('__LOG_SLOW_WIDGET__')) if(!defined('__DEBUG_QUERY__')) if(!defined('__OB_GZHANDLER_ENABLE__')) if(!defined('__ENABLE_PHPUNIT_TEST__')) if(!defined('__PROXY_SERVER__')) if(!defined('__ERROR_LOG__')) if(!defined('__DISABLE_DEFAULT_CSS__')) if(!defined('__AUTO_OPCACHE_INVALIDATE__')) if((__DEBUG_OUTPUT__==2)&&version_compare(PHP_VERSION, '6.0.0')===-1) if(version_compare(PHP_VERSION, '5.3.0') >=0) $GLOBALS['__xe_autoload_file_map']
Definition: config.inc.php:324
$output
Definition: ko.install.php:193
& getInstance($target= 'object', $info=null, $always_use_file=false)
writeFile($filename, $buff, $mode="w")
compile($tpl_path, $tpl_filename, $tpl_file= '')
init($tpl_path, $tpl_filename, $tpl_file= '')
compileDirect($tpl_path, $tpl_filename)
escape_sqstr($str)
Definition: func.inc.php:1847
getScriptPath()
Definition: func.inc.php:1364
const _XE_PATH_
Definition: config.inc.php:49
readFile($filename)