*/ /** * @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 $skipTags = NULL; private $handler_mtime = 0; static private $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; } /** * returns TemplateHandler's singleton object * @return TemplateHandler instance */ static public function &getInstance() { static $oTemplate = NULL; if(__DEBUG__ == 3) { 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(substr($tpl_path, -1) != '/') { $tpl_path .= '/'; } if(!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 = '') { $buff = false; // store the starting time for debug information if(__DEBUG__ == 3) { $start = getMicroTime(); } // 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; // cache control $oCacheHandler = CacheHandler::getInstance('template'); // get cached buff if($oCacheHandler->isSupport()) { $cache_key = 'template:' . $this->file; $buff = $oCacheHandler->get($cache_key, $latest_mtime); } else { if(is_readable($this->compiled_file) && filemtime($this->compiled_file) > $latest_mtime && filesize($this->compiled_file)) { $buff = 'file://' . $this->compiled_file; } } if($buff === FALSE) { $buff = $this->parse(); if($oCacheHandler->isSupport()) { $oCacheHandler->put($cache_key, $buff); } else { FileHandler::writeFile($this->compiled_file, $buff); } } $output = $this->_fetch($buff); if($__templatehandler_root_tpl == $this->file) { $__templatehandler_root_tpl = null; } // store the ending time for debug information if(__DEBUG__ == 3) { $GLOBALS['__template_elapsed__'] += getMicroTime() - $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'); } // replace comments $buff = preg_replace('@@s', '', $buff); // replace value of src in img/input/script tag $buff = preg_replace_callback('/<(?:img|input|script)[^<>]*src="(?!https?:\/\/|[\/\{])([^"]+)"/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)?))(?(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('/(