15 private $compiled_path =
'files/cache/template_compiled/';
17 private $filename = 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;
34 ini_set(
'pcre.jit',
"0");
36 $this->compiled_path =
_XE_PATH_ . $this->compiled_path;
37 $this->config =
new stdClass();
39 $this->ignoreEscape = array(
40 'functions' =>
function ($m) {
45 return preg_match(
'/^(' . implode(
'|', $list) .
')\(/', $m[1]);
47 'lang' =>
function ($m) {
49 return preg_match(
'/^\$lang\->/', trim($m[1]));
62 static $oTemplate = NULL;
66 if(!isset(
$GLOBALS[
'__TemplateHandlerCalled__']))
68 $GLOBALS[
'__TemplateHandlerCalled__'] = 1;
72 $GLOBALS[
'__TemplateHandlerCalled__']++;
91 protected function init($tpl_path, $tpl_filename, $tpl_file =
'')
94 if(substr($tpl_path, -1) !=
'/')
98 if(!is_dir($tpl_path))
102 if(!file_exists($tpl_path . $tpl_filename) && file_exists($tpl_path . $tpl_filename .
'.html'))
104 $tpl_filename .=
'.html';
110 $tpl_file = $tpl_path . $tpl_filename;
114 $this->path = $tpl_path;
115 $this->filename = $tpl_filename;
116 $this->
file = $tpl_file;
118 $this->web_path = $this->xe_path .
'/' . ltrim(preg_replace(
'@^' . preg_quote(
_XE_PATH_,
'@') .
'|\./@',
'', $this->path),
'/');
122 $this->compiled_file =
"{$this->compiled_path}{$hash}.compiled.php";
127 $this->handler_mtime = filemtime(__FILE__);
139 public function compile($tpl_path, $tpl_filename, $tpl_file =
'')
150 $this->
init($tpl_path, $tpl_filename, $tpl_file);
153 if(!$this->
file || !file_exists($this->
file))
155 return "Err : '{$this->file}' template file does not exists.";
159 if(is_null(self::$rootTpl))
161 self::$rootTpl = $this->file;
164 $source_template_mtime = filemtime($this->
file);
165 $latest_mtime = $source_template_mtime > $this->handler_mtime ? $source_template_mtime : $this->handler_mtime;
171 if($oCacheHandler->isSupport())
173 $cache_key =
'template:' . $this->file;
174 $buff = $oCacheHandler->get($cache_key, $latest_mtime);
178 if(is_readable($this->compiled_file) && filemtime($this->compiled_file) > $latest_mtime && filesize($this->compiled_file))
180 $buff =
'file://' . $this->compiled_file;
186 $buff = $this->
parse();
187 if($oCacheHandler->isSupport())
189 $oCacheHandler->put($cache_key, $buff);
197 $output = $this->_fetch($buff);
199 if($__templatehandler_root_tpl == $this->
file)
201 $__templatehandler_root_tpl = null;
221 $this->
init($tpl_path, $tpl_filename, null);
224 if(!$this->
file || !file_exists($this->
file))
227 exit(
"Cannot find the template file: '{$this->file}'");
230 return $this->
parse();
238 protected function parse($buff = null)
242 if(!is_readable($this->
file))
252 if(is_null($this->skipTags))
254 $this->skipTags = array(
'marquee');
258 $previous_config = clone $this->config;
259 $this->config =
new stdClass();
260 $this->config->autoescape = null;
262 if(preg_match(
'/<config( [^>\/]+)/', $buff, $config_match))
264 if(preg_match_all(
'@ (?<name>\w+)="(?<value>[^"]+)"@', $config_match[1], $config_matches, PREG_SET_ORDER))
266 foreach($config_matches as $config_match)
268 if($config_match[
'name'] ===
'autoescape')
270 $this->config->autoescape = $config_match[
'value'];
276 if($this->config->autoescape ===
'on') $this->autoescape =
true;
279 $buff = preg_replace(
'@<!--//.*?-->@s',
'', $buff);
282 $buff = preg_replace_callback(
'/<(?:img|input|script)(?:[^<>]*?)(?(?=cond=")(?:cond="[^"]+"[^<>]*)+|)[^<>]* src="(?!(?:https?|file):\/\/|[\/\{])([^"]+)"/is', array($this,
'_replacePath'), $buff);
285 $buff = $this->_parseInline($buff);
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);
291 $buff = preg_replace(
'@</?block\s*>@is',
'', $buff);
294 $temp = preg_replace_callback(
'/(<form(?:<\?php.+?\?>|[^<>]+)*?>)(.*?)(<\/form>)/is', array($this,
'_compileFormAuthGeneration'), $buff);
301 $buff =
'<?php if(!defined("__XE__"))exit;?>' . $buff;
304 $buff = preg_replace(array(
'/(\n|\r\n)+/',
'/(;)?( )*\?><\?php([\n\t ]+)?/'), array(
"\n",
";\n"), $buff);
307 $this->config = $previous_config;
321 private function _compileFormAuthGeneration($matches)
326 preg_match(
'/ruleset="([^"]*?)"/is', $matches[1], $m);
329 $matches[1] = preg_replace(
'/' . addcslashes($m[0],
'?$') .
'/i',
'', $matches[1]);
331 if(strpos($m[1],
'@') !== FALSE)
333 $path = str_replace(
'@',
'', $m[1]);
334 $path =
'./files/ruleset/' . $path .
'.xml';
336 else if(strpos($m[1],
'#') !== FALSE)
338 $fileName = str_replace(
'#',
'', $m[1]);
339 $fileName = str_replace(
'<?php echo ',
'', $fileName);
340 $fileName = str_replace(
' ?>',
'', $fileName);
341 $path =
'#./files/ruleset/' . $fileName .
'.xml';
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';
349 else if(preg_match(
'@(?:^|\.?/)(modules/[\w-]+)@', $this->path, $mm))
351 $module_path = $mm[1];
352 $path = $module_path .
'/ruleset/' . $m[1] .
'.xml';
355 $matches[2] =
'<input type="hidden" name="ruleset" value="' . $m[1] .
'" />' . $matches[2];
357 $matches[1] =
'<?php Context::addJsFile("' . $path .
'", FALSE, "", 0, "body", TRUE, "' . $autoPath .
'") ?' .
'>' . $matches[1];
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))
367 $generatedHidden =
'';
368 foreach($resultArray AS $key => $value)
370 $generatedHidden .=
'<input type="hidden" name="' . $value .
'" value="<?php echo $__Context->' . $value .
' ?>" />';
372 $matches[2] = $generatedHidden . $matches[2];
376 if(!preg_match(
'/no-error-return-url="true"/i', $matches[1]))
378 preg_match(
'/<input[^>]*name="error_return_url"[^>]*>/is', $matches[2], $m3);
380 $matches[2] =
'<input type="hidden" name="error_return_url" value="<?php echo htmlspecialchars(getRequestUriByServerEnviroment(), ENT_COMPAT | ENT_HTML401, \'UTF-8\', false) ?>" />' . $matches[2];
384 $matches[1] = preg_replace(
'/no-error-return-url="true"/i',
'', $matches[1]);
388 return implode($matches);
396 private function _fetch($buff)
403 $__Context = &
$GLOBALS[
'__Context__'];
404 $__Context->tpl_path = $this->path;
406 if($_SESSION[
'is_logged'])
411 $level = ob_get_level();
413 if(substr($buff, 0, 7) ==
'file://')
419 $eval_str_buffed =
"?>" . $eval_str;
420 @eval($eval_str_buffed);
421 $error_info = error_get_last();
423 if ($error_info[
'type'] == 4)
425 throw new Exception(
"Error Parsing Template - {$error_info['message']} in template file {$this->file}");
430 include(substr($buff, 7));
435 $eval_str =
"?>" . $buff;
437 $error_info = error_get_last();
439 if ($error_info[
'type'] == 4)
441 throw new Exception(
"Error Parsing Template - {$error_info['message']} in template file {$this->file}");
446 while (ob_get_level() - $level > 0) {
447 $contents .= ob_get_contents();
461 private function _replacePath($match)
464 if(preg_match(
'@^\${@', $match[1]))
470 if(preg_match(
'@^[\'|"]\s*\.\s*\$@', $match[1]))
475 $src = preg_replace(
'@^(\./)+@',
'', trim($match[1]));
477 $src = $this->web_path . $src;
478 $src = str_replace(
'/./',
'/', $src);
481 $src = preg_replace(
'@/((?:[\w-]+/)+)\1@',
'/\1', $src);
483 while(($tmp = preg_replace(
'@[^/]+/\.\./@',
'', $src, 1)) !== $src)
488 return substr($match[0], 0, -strlen($match[1]) - 6) .
"src=\"{$src}\"";
496 private function _parseInline($buff)
498 if(!preg_match_all(
'/<([a-zA-Z]+\d?)(?:\s)/', $buff, $match))
503 $tags = array_diff(array_unique($match[1]), $this->skipTags);
510 $tags =
'(?:' . implode(
'|', $tags) .
')';
511 $split_regex =
"@(<(?>/?{$tags})(?>[^<>\{\}\"']+|<!--.*?-->|{[^}]+}|\".*?\"|'.*?'|.)*?>)@s";
513 $nodes = preg_split($split_regex, $buff, -1, PREG_SPLIT_DELIM_CAPTURE);
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);
518 for($idx = 1, $node_len = count($nodes); $idx < $node_len; $idx+=2)
520 if(!($node = $nodes[$idx]))
525 if(preg_match_all(
'@\s(loop|cond)="([^"]+)"@', $node, $matches))
528 $tag = substr($node, 1, strpos($node,
' ') - 1);
534 foreach($matches[1] as $n => $stmt)
536 $expr = $matches[2][$n];
543 $nodes[$idx - 1] .=
"<?php if({$expr}){ ?>";
546 if(!preg_match(
'@^(?:(.+?)=>(.+?)(?:,(.+?))?|(.*?;.*?;.*?)|(.+?)\s*=\s*(.+?))$@', $expr, $expr_m))
552 $expr_m[1] = trim($expr_m[1]);
553 $expr_m[2] = trim($expr_m[2]);
556 $expr_m[2] .=
'=>' . trim($expr_m[3]);
558 $nodes[$idx - 1] .=
"<?php if({$expr_m[1]}&&count({$expr_m[1]}))foreach({$expr_m[1]} as {$expr_m[2]}){ ?>";
562 $nodes[$idx - 1] .=
"<?php for({$expr_m[4]}){ ?>";
566 $nodes[$idx - 1] .=
"<?php while({$expr_m[5]}={$expr_m[6]}){ ?>";
571 $node = preg_replace(
'@\s(loop|cond)="([^"]+)"@',
'', $node);
574 $close_php =
'<?php ' . str_repeat(
'}', $closing) .
' ?>';
576 if($node{1} ==
'!' || substr($node, -2, 1) ==
'/' || isset($self_closing[$tag]))
578 $nodes[$idx + 1] = $close_php . $nodes[$idx + 1];
583 for($i = $idx + 2; $i < $node_len; $i+=2)
586 if(strpos($nd, $tag) === 1)
590 elseif(strpos($nd,
'/' . $tag) === 1)
595 $nodes[$i - 1] .= $nodes[$i] . $close_php;
604 if(strpos($node,
'|cond="') !==
false)
606 $node = preg_replace(
'@(\s[-\w:]+(?:="[^"]+?")?)\|cond="(.+?)"@s',
'<?php if($2){ ?>$1<?php } ?>', $node);
610 if($nodes[$idx] != $node)
612 $nodes[$idx] = $node;
616 $buff = implode(
'', $nodes);
627 private function _parseResource($m)
629 $escape_option =
'noescape';
631 if($this->autoescape)
633 $escape_option =
'autoescape';
637 if ($this->config->autoescape ===
'off') {
638 $escape_option =
'noescape';
644 if(preg_match(
'@^(\w+)\(@', $m[1], $mm) && !function_exists($mm[1]))
652 return "<?php {$m[1]} ?>";
657 foreach ($this->ignoreEscape as $key => $value)
659 if($this->ignoreEscape[$key]($m))
661 $escape_option =
'noescape';
667 if (preg_match(
'@^(.+?)(?<![|\s])((?:\|[a-z]{2}[a-z0-9_]+(?::.+)?)+)$@', $m[1], $mm))
678 $var = self::_replaceVar($m[1]);
681 foreach ($filters as $filter)
684 if (preg_match(
'/^([a-z0-9_-]+):(.+)$/', $filter, $matches))
686 $filter = $matches[1];
687 $filter_option = $matches[2];
688 if (!self::_isVar($filter_option) && !preg_match(
"/^'.*'$/", $filter_option) && !preg_match(
'/^".*"$/', $filter_option))
690 $filter_option =
"'" .
escape_sqstr($filter_option) .
"'";
694 $filter_option = self::_replaceVar($filter_option);
699 $filter_option = null;
709 $escape_option = $filter;
713 $var =
"escape_js({$var})";
714 $escape_option =
'noescape';
718 $var =
"json_encode({$var})";
719 $escape_option =
'noescape';
724 $var = $filter_option ?
"strip_tags({$var}, {$filter_option})" :
"strip_tags({$var})";
728 $var =
"trim({$var})";
732 $var =
"rawurlencode({$var})";
733 $escape_option =
'noescape';
737 $var =
"strtolower({$var})";
741 $var =
"strtoupper({$var})";
745 $var = $this->_applyEscapeOption($var, $escape_option);
746 $var =
"nl2br({$var})";
747 $escape_option =
'noescape';
751 $var = $filter_option ?
"implode({$filter_option}, {$var})" :
"implode(', ', {$var})";
760 case 'number_format':
761 $var = $filter_option ?
"number_format({$var}, {$filter_option})" :
"number_format({$var})";
762 $escape_option =
'noescape';
766 $var = $this->_applyEscapeOption($var,
'autoescape');
769 $filter_option = $this->_applyEscapeOption($filter_option,
'autoescape');
770 $var =
"'<a href=\"' . {$filter_option} . '\">' . {$var} . '</a>'";
774 $var =
"'<a href=\"' . {$var} . '\">' . {$var} . '</a>'";
776 $escape_option =
'noescape';
781 $var =
"'INVALID FILTER ({$filter})'";
782 $escape_option =
'noescape';
787 return '<?php echo ' . $this->_applyEscapeOption($var, $escape_option) .
' ?>';
796 if(preg_match_all(
'@,(\w+)="([^"]+)"@', $m[6], $mm))
798 foreach($mm[1] as $idx => $name)
800 $attr[$name] = $mm[2][$idx];
803 $attr[
'target'] = $m[5];
807 if(!preg_match_all(
'@ (\w+)="([^"]+)"@', $m[6], $mm))
811 foreach($mm[1] as $idx => $name)
813 $attr[$name] = $mm[2][$idx];
821 if(!$this->
file || !$attr[
'target'])
826 $pathinfo = pathinfo($attr[
'target']);
834 return "<?php \$__tpl=TemplateHandler::getInstance();echo \$__tpl->compile('{$fileDir}','{$pathinfo['basename']}') ?>";
836 case 'load_js_plugin':
838 $s =
"<!--#JSPLUGIN:{$plugin}-->";
839 if(strpos($plugin,
'$__Context') ===
false)
841 $plugin =
"'{$plugin}'";
844 $s .=
"<?php Context::loadJavascriptPlugin({$plugin}); ?>";
851 $pathinfo = pathinfo($attr[
'target']);
852 $doUnload = ($m[3] ===
'unload');
853 $isRemote = !!preg_match(
'@^(https?:)?//@i', $attr[
'target']);
857 if(!preg_match(
'@^\.?/@', $attr[
'target']))
859 $attr[
'target'] =
'./' . $attr[
'target'];
861 if(substr($attr[
'target'], -5) ==
'/lang')
863 $pathinfo[
'dirname'] .=
'/lang';
864 $pathinfo[
'basename'] =
'';
865 $pathinfo[
'extension'] =
'xml';
870 $attr[
'target'] = $relativeDir .
'/' . $pathinfo[
'basename'];
873 switch($pathinfo[
'extension'])
876 if($isRemote || $doUnload)
881 if($pathinfo[
'basename'] ==
'lang.xml' || substr($pathinfo[
'dirname'], -5) ==
'/lang')
883 $result =
"Context::loadLang('{$relativeDir}');";
887 $result =
"require_once('./classes/xml/XmlJsFilter.class.php');\$__xmlFilter=new XmlJsFilter('{$relativeDir}','{$pathinfo['basename']}');\$__xmlFilter->compile();";
893 $result =
"Context::unloadFile('{$attr['target']}','{$attr['targetie']}');";
897 $metafile = $attr[
'target'];
898 $result =
"\$__tmp=array('{$attr['target']}','{$attr['type']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);";
904 $result =
"Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');";
908 $metafile = $attr[
'target'];
909 $result =
"\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);";
914 $result =
"<?php {$result} ?>";
917 $result =
"<!--#Meta:{$metafile}-->" . $result;
924 if(preg_match_all(
'@ (\w+)="([^"]+)"@', $m[6], $config_matches, PREG_SET_ORDER))
926 foreach($config_matches as $config_match)
928 $result .=
"\$this->config->{$config_match[1]} = '" . trim(strtolower($config_match[2])) .
"';";
931 return "<?php {$result} ?>";
938 $m[7] = substr($m[7], 1);
941 return '<?php ' . $this->
_replaceVar($m[8]) .
'{ ?>' . $m[9];
943 if(!preg_match(
'/^(?:((?:end)?(?:if|switch|for(?:each)?|while)|end)|(else(?:if)?)|(break@)?(case|default)|(break))$/', $m[7], $mm))
951 return '<?php } ?>' . $m[9];
955 if($mm[1] ==
'switch')
959 elseif($mm[1] ==
'foreach')
961 $var = preg_replace(
'/^\s*\(\s*(.+?) .*$/',
'$1', $m[8]);
962 $precheck =
"if({$var}&&count({$var}))";
964 return '<?php ' . $this->
_replaceVar($precheck . $m[7] . $m[8]) .
'{ ?>' . $m[9];
968 return "<?php }{$m[7]}" . $this->
_replaceVar($m[8]) .
"{ ?>" . $m[9];
972 return "<?php " . ($mm[3] ?
'break;' :
'') .
"{$m[7]} " . trim($m[8],
'()') .
": ?>" . $m[9];
976 return "<?php break; ?>";
986 private function _applyEscapeOption($str, $escape_option =
'noescape')
988 switch($escape_option)
991 return "escape({$str}, true)";
995 return "escape({$str}, false)";
998 return "(\$this->config->autoescape === 'on' ? escape({$str}, false) : ({$str}))";
1011 $fileDir = strtr(realpath($this->path),
'\\',
'/');
1014 $path = strtr(realpath($fileDir .
'/' . $path),
'\\',
'/');
1020 $dirs = explode(
'/', $fileDir);
1021 $paths = explode(
'/', $_path);
1022 $idx = array_search($paths[0], $dirs);
1026 while($dirs[$idx] && $dirs[$idx] === $paths[0])
1028 array_splice($dirs, $idx, 1);
1029 array_shift($paths);
1031 $path = strtr(realpath($fileDir .
'/' . implode(
'/', $paths)),
'\\',
'/');
1035 $path = preg_replace(
'/^' . preg_quote(
_XE_PATH_,
'/') .
'/',
'', $path);
1046 private static function _isVar($str)
1048 return preg_match(
'@(?<!::|\\\\|(?<!eval\()\')\$([a-z_][a-z0-9_]*)@i', $str) ?
true :
false;
1062 return preg_replace(
'@(?<!::|\\\\|(?<!eval\()\')\$([a-z]|_[a-z0-9])@i',
'\$__Context->$1', $php);
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)';
1072 if(preg_match(
'/^(\.\/)?(modules\/' . $dirTpl .
'|common)\/tpl\//', $absPath))
1078 if(preg_match(
'/^(\.\/)?\(' . $dirSkin .
'\//', $absPath))
1088 $this->autoescape = $val;
explode_with_escape($delimiter, $str, $limit=0, $escape_char= '\\')
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']
& 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)