*/ /** * @class TemplateHandler * @author NAVER (developers@xpressengine.com) * template compiler * @version 0.1 * @remarks It compiles template file by using regular expression into php * code, and XE caches compiled code for further uses */ class TemplateHandler { private $compiled_path = 'files/cache/template_compiled/'; ///< path of compiled caches files private $path = NULL; ///< target directory private $filename = NULL; ///< target filename private $file = NULL; ///< target file (fullpath) private $xe_path = NULL; ///< XpressEngine base path private $web_path = NULL; ///< tpl file web path private $compiled_file = NULL; ///< tpl file web path private $config = NULL; private $skipTags = NULL; private $handler_mtime = 0; private static $rootTpl = NULL; /** * constructor * @return void */ public function __construct() { $this->xe_path = rtrim(preg_replace('/([^\.^\/]+)\.php$/i', '', $_SERVER['SCRIPT_NAME']), '/'); $this->compiled_path = _XE_PATH_ . $this->compiled_path; $this->config = new stdClass(); } /** * returns TemplateHandler's singleton object * @return TemplateHandler instance */ public static function getInstance() { static $oTemplate = NULL; if(!isset($GLOBALS['__TemplateHandlerCalled__'])) { $GLOBALS['__TemplateHandlerCalled__'] = 1; } else { $GLOBALS['__TemplateHandlerCalled__']++; } if(!$oTemplate) { $oTemplate = new TemplateHandler(); } return $oTemplate; } /** * set variables for template compile * @param string $tpl_path * @param string $tpl_filename * @param string $tpl_file * @return void */ protected function init($tpl_path, $tpl_filename, $tpl_file = '') { // verify arguments if(!$tpl_path || substr($tpl_path, -1) != '/') { $tpl_path .= '/'; } if($tpl_path === '/' || !is_dir($tpl_path)) { return; } if(!file_exists($tpl_path . $tpl_filename) && file_exists($tpl_path . $tpl_filename . '.html')) { $tpl_filename .= '.html'; } // create tpl_file variable if(!$tpl_file) { $tpl_file = $tpl_path . $tpl_filename; } // set template file infos. $this->path = $tpl_path; $this->filename = $tpl_filename; $this->file = $tpl_file; $this->web_path = $this->xe_path . '/' . ltrim(preg_replace('@^' . preg_quote(_XE_PATH_, '@') . '|\./@', '', $this->path), '/'); // get compiled file name $hash = md5($this->file . __XE_VERSION__); $this->compiled_file = "{$this->compiled_path}{$hash}.compiled.php"; // compare various file's modified time for check changed $this->handler_mtime = filemtime(__FILE__); $skip = array(''); } /** * compiles specified tpl file and execution result in Context into resultant content * @param string $tpl_path path of the directory containing target template file * @param string $tpl_filename target template file's name * @param string $tpl_file if specified use it as template file's full path * @return string Returns compiled result in case of success, NULL otherwise */ public function compile($tpl_path, $tpl_filename, $tpl_file = '') { // store the starting time for debug information $start = microtime(true); // initiation $this->init($tpl_path, $tpl_filename, $tpl_file); // if target file does not exist exit if(!$this->file || !file_exists($this->file)) { return "Err : '{$this->file}' template file does not exists."; } // for backward compatibility if(is_null(self::$rootTpl)) { self::$rootTpl = $this->file; } $source_template_mtime = filemtime($this->file); $latest_mtime = $source_template_mtime > $this->handler_mtime ? $source_template_mtime : $this->handler_mtime; // get cached file if(!file_exists($this->compiled_file) || filemtime($this->compiled_file) < $latest_mtime) { FileHandler::writeFile($this->compiled_file, $this->parse()); } $output = $this->_fetch($this->compiled_file); if($__templatehandler_root_tpl == $this->file) { $__templatehandler_root_tpl = null; } // store the ending time for debug information $GLOBALS['__template_elapsed__'] += microtime(true) - $start; return $output; } /** * compile specified file and immediately return * @param string $tpl_path path of the directory containing target template file * @param string $tpl_filename target template file's name * @return string Returns compiled content in case of success or NULL in case of failure */ public function compileDirect($tpl_path, $tpl_filename) { $this->init($tpl_path, $tpl_filename, null); // if target file does not exist exit if(!$this->file || !file_exists($this->file)) { Context::close(); exit("Cannot find the template file: '{$this->file}'"); } return $this->parse(); } /** * parse syntax. * @param string $buff template file * @return string compiled result in case of success or NULL in case of error */ protected function parse($buff = null) { if(is_null($buff)) { if(!is_readable($this->file)) { return; } // read tpl file $buff = FileHandler::readFile($this->file); } // HTML tags to skip if(is_null($this->skipTags)) { $this->skipTags = array('marquee'); } // reset config for this buffer (this step is necessary because we use a singleton for every template) $previous_config = clone $this->config; $this->config = new stdClass(); // detect existence of autoescape config $this->config->autoescape = (strpos($buff, ' autoescape="') === FALSE) ? NULL : 'off'; // replace comments $buff = preg_replace('@@s', '', $buff); // replace value of src in img/input/script tag $buff = preg_replace_callback('/<(?:img|input|script)(?:[^<>]*?)(?(?=cond=")(?:cond="[^"]+"[^<>]*)+|)[^<>]* src="(?!(?:https?|file):\/\/|[\/\{])([^"]+)"/is', array($this, '_replacePath'), $buff); // replace loop and cond template syntax $buff = $this->_parseInline($buff); // include, unload/load, import $buff = preg_replace_callback('/{(@[\s\S]+?|(?=\$\w+|_{1,2}[A-Z]+|[!\(+-]|\w+(?:\(|::)|\d+|[\'"].*?[\'"]).+?)}|<(!--[#%])?(include|import|(un)?load(?(4)|(?:_js_plugin)?)|config)(?(2)\(["\']([^"\']+)["\'])(.*?)(?(2)\)--|\/)>|(\s*)/', array($this, '_parseResource'), $buff); // remove block which is a virtual tag $buff = preg_replace('@?block\s*>@is', '', $buff); // form auto generation $temp = preg_replace_callback('/(