diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php index 847a7d628..2d181bb9b 100644 --- a/classes/template/TemplateHandler.class.php +++ b/classes/template/TemplateHandler.class.php @@ -8,7 +8,7 @@ * code, and XE caches compiled code for further uses **/ - class TemplateHandler extends Handler { + class TemplateHandler { var $compiled_path = './files/cache/template_compiled/'; ///< path of compiled caches files @@ -18,30 +18,42 @@ var $xe_path = null; ///< XpressEngine base path var $web_path = null; ///< tpl file web path var $compiled_file = null; ///< tpl file web path - var $buff = null; ///< tpl file web path var $handler_mtime = 0; + function TemplateHandler() + { + // TODO: replace this with static variable in PHP5 + global $__templatehandler_root_tpl; + + $__templatehandler_root_tpl = null; + + $this->xe_path = rtrim(preg_replace('/([^\.^\/]+)\.php$/i','',$_SERVER['SCRIPT_NAME']),'/'); + } + /** * @brief returns TemplateHandler's singleton object * @return TemplateHandler instance **/ - function &getInstance() { + function &getInstance() + { + static $oTemplate = null; + if(__DEBUG__==3 ) { if(!isset($GLOBALS['__TemplateHandlerCalled__'])) $GLOBALS['__TemplateHandlerCalled__']=1; else $GLOBALS['__TemplateHandlerCalled__']++; } - if(!$GLOBALS['__TemplateHandler__']) { - $GLOBALS['__TemplateHandler__'] = new TemplateHandler(); - } - return $GLOBALS['__TemplateHandler__']; + if(!$oTemplate) $oTemplate = new TemplateHandler(); + + return $oTemplate; } /** * @brief set variables for template compile **/ - function init($tpl_path, $tpl_filename, $tpl_file) { + function init($tpl_path, $tpl_filename, $tpl_file='') + { // verify arguments if(substr($tpl_path,-1)!='/') $tpl_path .= '/'; if(!file_exists($tpl_path.$tpl_filename)&&file_exists($tpl_path.$tpl_filename.'.html')) $tpl_filename .= '.html'; @@ -50,22 +62,18 @@ if(!$tpl_file) $tpl_file = $tpl_path.$tpl_filename; // set template file infos. - $info = pathinfo($tpl_file); - //$this->path = preg_replace('/^\.\//','',$info['dirname']).'/'; $this->path = $tpl_path; $this->filename = $tpl_filename; $this->file = $tpl_file; - $this->xe_path = preg_replace('/([^\.^\/]+)\.php$/i','',$_SERVER['SCRIPT_NAME']); - $this->web_path = $this->xe_path.str_replace(_XE_PATH_,'',$this->path); + $this->web_path = $this->xe_path.'/'.ltrim(preg_replace('@^'.preg_quote(_XE_PATH_,'@').'|\./@','',$this->path),'/'); // get compiled file name - $this->compiled_file = sprintf('%s%s.compiled.php',$this->compiled_path, md5($this->file . __ZBXE_VERSION__)); + $hash = md5($this->file . __ZBXE_VERSION__); + $this->compiled_file = "{$this->compiled_path}{$hash}.compiled.php"; // compare various file's modified time for check changed - $this->handler_mtime = filemtime(_XE_PATH_.'classes/template/TemplateHandler.class.php'); - - $this->buff = null; + $this->handler_mtime = filemtime(__FILE__); } /** @@ -75,7 +83,11 @@ * @param[in] $tpl_file if specified use it as template file's full path * @return Returns compiled result in case of success, NULL otherwise */ - function compile($tpl_path, $tpl_filename, $tpl_file = '') { + function compile($tpl_path, $tpl_filename, $tpl_file='') { + global $__templatehandler_root_tpl; + + $buff = ''; + // store the starting time for debug information if(__DEBUG__==3 ) $start = getMicroTime(); @@ -83,31 +95,40 @@ $this->init($tpl_path, $tpl_filename, $tpl_file); // if target file does not exist exit - if(!$this->file || !file_exists($this->file)) return sprintf('Err : "%s" template file does not exists.', $this->file); + if(!$this->file || !file_exists($this->file)) return "Err : '{$this->file}' template file does not exists."; + + // for backward compatibility + if(is_null($__templatehandler_root_tpl)) { + $__templatehandler_root_tpl = $this->file; + } $source_template_mtime = filemtime($this->file); $latest_mtime = $source_template_mtime>$this->handler_mtime?$source_template_mtime:$this->handler_mtime; - // cache controll + // cache control $oCacheHandler = &CacheHandler::getInstance('template'); // get cached buff if($oCacheHandler->isSupport()){ $cache_key = 'template:'.$this->file; - $this->buff = $oCacheHandler->get($cache_key, $latest_mtime); + $buff = $oCacheHandler->get($cache_key, $latest_mtime); } else { - if(file_exists($this->compiled_file) && filemtime($this->compiled_file)>$latest_mtime) { - $this->buff = FileHandler::readFile($this->compiled_file); + if(is_readable($this->compiled_file) && filemtime($this->compiled_file)>$latest_mtime && filesize($this->compiled_file)) { + $buff = 'file://'.$this->compiled_file; } } - if(!$this->buff) { - $this->parse(); - if($oCacheHandler->isSupport()) $oCacheHandler->put($cache_key, $this->buff); - else FileHandler::writeFile($this->compiled_file, $this->buff); + if(!$buff) { + $buff = $this->parse(); + if($oCacheHandler->isSupport()) $oCacheHandler->put($cache_key, $buff); + else FileHandler::writeFile($this->compiled_file, $buff); } - $output = $this->_fetch(); + $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; @@ -127,12 +148,10 @@ // if target file does not exist exit if(!$this->file || !file_exists($this->file)) { Context::close(); - printf('"%s" template file is not exists.', $this->file); - exit(); + exit("Cannot find the template file: '{$this->file}'"); } - $this->parse(); - return $this->buff; + return $this->parse(); } /** @@ -142,65 +161,36 @@ * @param[in] $compiled_tpl_file if specified, write compiled result into the file * @return compiled result in case of success or NULL in case of error **/ - function parse() { - if(!file_exists($this->file)) return; + function parse($buff=null) { + if(is_null($buff)) { + if(!is_readable($this->file)) return; - // read tpl file - $buff = FileHandler::readFile($this->file); + // read tpl file + $buff = FileHandler::readFile($this->file); + } + + // replace comments + $buff = preg_replace('@@', '', $buff); // replace value of src in img/input/script tag - $buff = preg_replace_callback('/<(img|input|script)([^>]*)src="([^"]*?)"/is', array($this, '_replacePath'), $buff); + $buff = preg_replace_callback('/<(?:img|input|script)(?:(?!["\'\/]\s*>).)* src="(?!https?:\/\/|[\/\{])([^"]+)"/is', array($this, '_replacePath'), $buff); - // replace the loop template syntax - $buff = $this->_replaceLoop($buff); + // replace loop and cond template syntax + $buff = $this->_parseInline($buff); - // |replace the cond template syntax - $buff = $this->_replaceCond($buff); + // include, unload/load, import + $buff = preg_replace_callback('/{(@[\s\S]+?|(?=\$\w+|\w+(?:\(|::)|\d+|[\'"].*?[\'"]).+?)}|<(!--[#%])?(include|import|(un)?load(?(4)|(?:_js_plugin)?))(?(2)\("([^"]+)")(.*?)(?(2)\)--|\/)>|/', array($this, '_parseResource'), $buff); - // replace the cond template syntax - $buff = preg_replace_callback("/<\/?(\w+)((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/i", array($this, '_replacePipeCond'), $buff); - - // replace the include tags - $buff = preg_replace_callback('!]+)>!is', array($this, '_replaceInclude'), $buff); - - // replace unload/load tags - $buff = preg_replace_callback('!<(unload|load) ([^>]+)>!is', array($this, '_replaceLoad'), $buff); - - // replace block which is a virtual tag - $buff = preg_replace('/|<\/block>/is','',$buff); - - // replace include - $buff = preg_replace_callback('!<\!--#include\(([^\)]*?)\)-->!is', array($this, '_compileIncludeToCode'), $buff); - - // replace - $buff = preg_replace_callback('!<\!--@(.*?)-->!is', array($this, '_compileFuncToCode'), $buff); - - // remove comments - $buff = preg_replace('!(\n?)( *?)<\!--//(.*?)-->!is', '', $buff); - - // import xml filter/ css/ js/ files (media is applied to only css) - $buff = preg_replace_callback('!<\!--%import\(\"([^\"]*?)\"(,optimized\=(true|false))?(,media\=\"([^\"]*)\")?(,targetie=\"([^\"]*)\")?(,index=\"([^\"]*)\")?(,type=\"([^\"]*)\")?\)-->!is', array($this, '_compileImportCode'), $buff); - - // unload css/ js (media is applied to only css) - $buff = preg_replace_callback('!<\!--%unload\(\"([^\"]*?)\"(,optimized\=(true|false))?(,media\=\"([^\"]*)\")?(,targetie=\"([^\"]*)\")?\)-->!is', array($this, '_compileUnloadCode'), $buff); - - // javascript plugin import - $buff = preg_replace_callback('!<\!--%load_js_plugin\(\"([^\"]*?)\"\)-->!is', array($this, '_compileLoadJavascriptPlugin'), $buff); + // remove block which is a virtual tag and remove comments + $buff = preg_replace('@|\s?@is','',$buff); // form auto generation $buff = preg_replace_callback('/(|[^<>]+)*?>)(.*?)(<\/form>)/is', array($this, '_compileFormAuthGeneration'), $buff); - // replace variables - $buff = preg_replace_callback('/\{[^@^ ]([^\{\}\n]+)\}/i', array($this, '_compileVarToContext'), $buff); - - // replace PHP variable types(converts characters like $ into shared context) - $buff = $this->_replaceVarInPHP($buff); - - // replace parts not displaying results - $buff = preg_replace_callback('/\{\@([^\{\}]+)\}/i', array($this, '_compileVarToSilenceExecute'), $buff); - // prevent from calling directly before writing into file - $this->buff = ''.$buff; + $buff = ''.$buff; + + return $buff; } /** @@ -241,14 +231,14 @@ $generatedHidden = ''; foreach($resultArray AS $key=>$value) { - $generatedHidden .= ''; + $generatedHidden .= ''; } $matches[2] = $generatedHidden.$matches[2]; } // return url generate preg_match('/]*name="error_return_url"[^>]*>/is', $matches[2], $m3); - if(!$m3[0]) $matches[2] = ''.$matches[2]; + if(!$m3[0]) $matches[2] = ''.$matches[2]; $matches[0] = ''; return implode($matches); @@ -261,17 +251,24 @@ * @param[in] $tpl_path set context's tpl path * @return result string **/ - function _fetch() { - if(!$this->buff) return; + function _fetch($buff) { + if(!$buff) return; $__Context = &$GLOBALS['__Context__']; $__Context->tpl_path = $this->path; - if($_SESSION['is_logged']) $__Context->logged_info = Context::get('logged_info'); + if($_SESSION['is_logged']) { + $__Context->logged_info = Context::get('logged_info'); + } ob_start(); - $eval_str = "?>".$this->buff; - eval($eval_str); + if(substr($buff, 0, 7) == 'file://') { + include(substr($buff, 7)); + } else { + $eval_str = "?>".$buff; + eval($eval_str); + } + return ob_get_clean(); } @@ -281,676 +278,262 @@ * @param[in] $matches match * @return changed result **/ - function _replacePath($matches) + function _replacePath($match) { - preg_match_all('/src="([^"]*?)"/is', $matches[0], $m); - for($i=0,$c=count($m[0]);$i<$c;$i++) { - $path = trim($m[1][$i]); - if(substr($path,0,1)=='/' || substr($path,0,1)=='{' || strpos($path,'://')!==false) continue; - if(substr($path,0,2)=='./') $path = substr($path,2); - $target = $this->web_path.$path; - while(strpos($target,'/../')!==false) - { - $target = preg_replace('/\/([^\/]+)\/\.\.\//','/',$target); - } - $target = str_replace('/./','/',$target); - $matches[0] = str_replace($m[0][$i], 'src="'.$target.'"', $matches[0]); - } - return $matches[0]; + $src = preg_replace('@^(\./)+@', '', trim($match[1])); + + $src = $this->web_path.$src; + $src = str_replace('/./', '/', $src); + + // for backward compatibility + $src = preg_replace('@((?:[\w-]+/)+)\1@', '\1', $src); + + while(($tmp=preg_replace('@[^/]+/\.\./@', '', $src))!==$src) $src = $tmp; + + return substr($match[0],0,-strlen($match[1])-6)."src=\"{$src}\""; } - /** - * @brief replace loop syntax - **/ - function _replaceLoop($buff) + function _parseInline($buff) { - while(false !== $pos = strpos($buff, ' loop="')) - { - $pre = substr($buff,0,$pos); - $next = substr($buff,$pos); + if(preg_match_all('/<([a-zA-Z]+\d?)(?>(?!<[a-z]+\d?[\s>]).)*?(?:[ \|]cond| loop)="/s', $buff, $match) === false) return $buff; - $pre_pos = strrpos($pre, '<'); + $tags = '(?:'.implode('|',array_unique($match[1])).')'; + $split_regex = "@(<(?>/?{$tags})(?>[^<>\{\}\"']+||{[^}]+}|\".*?\"|'.*?'|.)*?>)@s"; - preg_match('/^ loop="([^"]+)"/i',$next,$m); - $tag = substr($next,0,strlen($m[0])); - $next = substr($next,strlen($m[0])); - $next_pos = strpos($next, '<'); + $nodes = preg_split($split_regex, $buff, -1, PREG_SPLIT_DELIM_CAPTURE); - $tag = substr($pre, $pre_pos). $tag. substr($next, 0, $next_pos); - $pre = substr($pre, 0, $pre_pos); - $next = substr($next, $next_pos); + // list of self closing tags + $self_closing = explode(',', 'area,base,basefont,br,hr,input,img,link,meta,param,frame,col'); - $tag_name = trim(substr($tag,1,strpos($tag,' '))); - $tag_head = $tag_tail = ''; + for($idx=1,$node_len=count($nodes); $idx < $node_len; $idx+=2) { + if(!($node=$nodes[$idx])) continue; - if(!preg_match('/ loop="([^"]+)"/is',$tag)) { - print "Invalid XpressEngine Template Syntax
"; - print "File : ".$this->file."
"; - print "Code : ".htmlspecialchars($tag); - exit(); - } + if(preg_match_all('@\s(loop|cond)="([^"]+)"@', $node, $matches)) { + $closing = 0; - preg_match_all('/ loop="([^"]+)"/is',$tag,$m); - $tag = preg_replace('/ loop="([^"]+)"/is','', $tag); + // process opening tag + foreach($matches[1] as $n=>$stmt) { + $expr = $matches[2][$n]; + $expr = $this->_replaceVar($expr); + $closing++; - for($i=0,$c=count($m[0]);$i<$c;$i++) - { - $loop = $m[1][$i]; - if(false!== $fpos = strpos($loop,'=>')) - { - $target = trim(substr($loop,0,$fpos)); - $vars = trim(substr($loop,$fpos+2)); - if(false===strpos($vars,',')) - { - $tag_head .= ''; - $tag_tail .= ''; - } - else - { - $t = explode(',',$vars); - $tag_head .= ' '.trim($t[1]).') { ?>'; - $tag_tail .= ''; + switch($stmt) { + case 'cond': + $nodes[$idx-1] .= ""; + break; + case 'loop': + if(!preg_match('@^(?:(.+?)=>(.+?)(?:,(.+?))?|(.*?;.*?;.*?)|(.+?)\s*=\s*(.+?))$@', $expr, $expr_m)) break; + if($expr_m[1]) { + if($expr_m[3]) $expr_m[2] .= '=>'.$expr_m[3]; + $nodes[$idx-1] .= ""; + }elseif($expr_m[4]) { + $nodes[$idx-1] .= ""; + }elseif($expr_m[5]) { + $nodes[$idx-1] .= ""; + } + break; } } - elseif(false!==strpos($loop,';')) - { - $tag_head .= ''; - $tag_tail .= ''; - } - else - { - $t = explode('=',$loop); - if(count($t)==2) - { - $tag_head .= ''; - $tag_tail .= ''; + $node = preg_replace('@\s(loop|cond)="([^"]+)"@', '', $node); + + // this tag + $tag = substr($node, 1, strpos($node, ' ')-1); + + // find closing tag + $close_php = ''; + if($node{1} == '!' || substr($node,-2,1) == '/' || in_array($tag, $self_closing)) { // self closing tag + $nodes[$idx+1] = $close_php.$nodes[$idx+1]; + } else { + $depth = 1; + for($i=$idx+2; $i < $node_len; $i+=2) { + $nd = $nodes[$i]; + if(strpos($nd, $tag) === 1) { + $depth++; + } elseif(strpos($nd, '/'.$tag) === 1) { + $depth--; + if(!$depth) { + $nodes[$i-1] .= $nodes[$i].$close_php; + $nodes[$i] = ''; + break; + } + } } } } - if(substr(trim($tag),-2)!='/>') - { - while(false !== $close_pos = strpos($next, '')); - $tag .= $tmp_buff; - $next = substr($next, strlen($tmp_buff)); - if(substr_count($tag, '<'.$tag_name) == substr_count($tag,'$1', $node); + $node = $this->_replaceVar($node); } - $buff = $pre.$tag_head.$tag.$tag_tail.$next; - } - return $buff; - } - - /** - * @brief replace pipe cond and |cond= - **/ - function _replacePipeCond($matches) - { - if(strpos($matches[0],'|cond')!==false) { - while(strpos($matches[0],'|cond="')!==false) { - if(preg_match('/ (\w+)=\"([^\"]+)\"\|cond=\"([^\"]+)\"/is', $matches[0], $m)) - $matches[0] = str_replace($m[0], sprintf(' %s="%s"', $m[3], $m[1], $m[2]), $matches[0]); - } + if($nodes[$idx] != $node) $nodes[$idx] = $node; } - return $matches[0]; - } - - /** - * @brief replace cond syntax - **/ - function _replaceCond($buff) - { - while(false !== ($pos = strpos($buff, ' cond="'))) - { - $pre = substr($buff,0,$pos); - $next = substr($buff,$pos); - - $pre_pos = strrpos($pre, '<'); - - $isClosedTagUse = true; - preg_match('/<(\/|[!DOCTYPE]|[a-z])/i',$next,$m); - // if not use closed tag, find simple closed tag - if(!$m[0]) { - $isClosedTagUse = false; - preg_match('/[^->]\/?>/',$next,$m); - } - if(!$m[0]) return $buff; - if($isClosedTagUse) $next_pos = strpos($next, $m[0]); - else $next_pos = strpos($next, $m[0])+2; - - $tag = substr($pre, $pre_pos). substr($next, 0, $next_pos); - $pre = substr($pre, 0, $pre_pos); - $next = substr($next, $next_pos); - $tag_name = trim(substr($tag,1,strpos($tag,' '))); - $tag_head = $tag_tail = ''; - - if(preg_match_all('/ cond=\"([^\"]+)"/is',$tag,$m)) - { - for($i=0,$c=count($m[0]);$i<$c;$i++) - { - $tag_head .= ''; - $tag_tail .= ''; - } - } - - if(!preg_match('/ cond="([^"]+)"/is',$tag)) { - print "Invalid XpressEngine Template Syntax
"; - print "File : ".$this->file."
"; - print "Code : ".htmlspecialchars($tag); - exit(); - } - - $tag = preg_replace('/ cond="([^"]+)"/is','', $tag); - if(substr(trim($tag),-2)=='/>') - { - $buff = $pre.$tag_head.$tag.$tag_tail.$next; - } - else - { - while(false !== $close_pos = strpos($next, '')); - $tag .= $tmp_buff; - $next = substr($next, strlen($tmp_buff)); - - if(substr_count($tag, '<'.$tag_name) == substr_count($tag,'path.substr($target,0,$pos); + $echo = 'echo '; + if($m[1]{0} == '@') { + $echo = ''; + $m[1] = substr($m[1], 1); + } + return '_replaceVar($m[1]).' ?>'; } - return sprintf( - 'compile(\'%s\',\'%s\');%s'. - '?>%s', - "\n", - "\n", - $path, $filename, "\n", - "\n" - ); + if($m[3]) + { + $attr = array(); + if($m[5]) { + if(preg_match_all('@,(\w+)="([^"]+)"@', $m[6], $mm)) { + foreach($mm[1] as $idx=>$name) { + $attr[$name] = $mm[2][$idx]; + } + } + $attr['target'] = $m[5]; + } else { + if(!preg_match_all('@ (\w+)="([^"]+)"@', $m[6], $mm)) return $m[0]; + foreach($mm[1] as $idx=>$name) { + $attr[$name] = $mm[2][$idx]; + } + } + + switch($m[3]) + { + // or + case 'include': + if(!$this->file || !$attr['target']) return ''; + + $pathinfo = pathinfo($attr['target']); + $fileDir = $this->_getRelativeDir($pathinfo['dirname']); + + if(!$fileDir) return ''; + + return "compile('{$fileDir}','{$pathinfo['basename']}') ?>"; + + // + case 'load_js_plugin': + $plugin = $this->_replaceVar($m[5]); + if(strpos($plugin, '$__Context') === false) $plugin = "'{$plugin}'"; + + return ""; + + // or or or + case 'import': + case 'load': + case 'unload': + $metafile = ''; + $pathinfo = pathinfo($attr['target']); + $doUnload = ($m[3] === 'unload'); + $isRemote = !!preg_match('@^https?://@i', $attr['target']); + + if(!$isRemote) { + if(!preg_match('@^\.?/@',$attr['target'])) $attr['target'] = './'.$attr['target']; + + $relativeDir = $this->_getRelativeDir($pathinfo['dirname']); + + $attr['target'] = $relativeDir.'/'.$pathinfo['basename']; + } + + switch($pathinfo['extension']) + { + case 'xml': + if($isRemote || $doUnload) return ''; + // language file? + if($pathinfo['basename'] == 'lang.xml' && substr($pathinfo['dirname'],-5) == '/lang') { + $result = "Context::loadLang('{$relativeDir}');"; + } else { + $result = "require_once('./classes/xml/XmlJsFilter.class.php');\$__xmlFilter = new XmlJsFilter('{$relativeDir}','{$pathinfo['basename']}');\$__xmlFilter->compile();"; + } + break; + case 'js': + if($doUnload) { + $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}');"; + } else { + $metafile = $attr['target']; + $result = "\$__tmp=array('{$attr['target']}','{$attr['type']}','{$attr['targetie']}','{$attr['index']}','{$attr['usecdn']}','{$attr['cdnprefix']}','{$attr['cdnversion']}');Context::loadFile(\$__tmp);unset(\$__tmp);"; + } + break; + case 'css': + if($doUnload) { + $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');"; + } else { + $metafile = $attr['target']; + $result = "\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}','{$attr['usecdn']}','{$attr['cdnprefix']}','{$attr['cdnversion']}');Context::loadFile(\$__tmp);unset(\$__tmp);"; + } + break; + } + + $result = ""; + if($metafile) $result = "".$result; + + return $result; + } + } + + // such as , , + if($m[7]) + { + $m[7] = substr($m[7],1); + if(!preg_match('/^(?:((?:end)?(?:if|switch|for(?:each)?|while)|end)|(else(?:if)?)|(break@)?(case|default)|(break))$/', $m[7], $mm)) return ''; + if($mm[1]) { + if($mm[1]{0} == 'e') return ''; + + $precheck = ''; + if($mm[1] == 'foreach') { + $var = preg_replace('/^\s*\(\s*(.+?) .*$/', '$1', $m[8]); + $precheck = "if({$var}&&count({$var}))"; + } + return '_replaceVar($precheck.$m[7].$m[8]).'{ ?>'; + } + if($mm[2]) return "_replaceVar($m[8])."{ ?>"; + if($mm[4]) return ""; + if($mm[5]) return ""; + return ''; + } + + return $m[0]; } - /** - * @brief replace load tags - **/ - function _replaceLoad($matches) { - $output = $matches[0]; - if(!preg_match_all('/ ([^=]+)=\"([^\"]+)\"/is',$matches[0], $m)) return $matches[0]; + function _getRelativeDir($path) + { + $_path = $path; - $type = $matches[1]; - for($i=0,$c=count($m[1]);$i<$c;$i++) - { - if(!trim($m[1][$i])) continue; - $attrs[trim($m[1][$i])] = trim($m[2][$i]); - } + $fileDir = dirname(strtr(realpath($this->file),'\\','/')); + if($path{0} != '/') $path = strtr(realpath($fileDir.'/'.$path),'\\','/'); - if(!$attrs['target']) return $matches[0]; + // for backward compatibility + if(!$path) { + $dirs = explode('/', $fileDir); + $paths = explode('/', $_path); + $idx = array_search($paths[0], $dirs); - $web_path = $this->web_path; - $base_path = $this->path; - - $target = $attrs['target']; - if(!preg_match('/^(http|https)/i',$target)) - { - if(substr($target,0,2)=='./') $target = substr($target,2); - //if(substr($target,0,1)!='/') $target = $web_path.$target; - } - - if(!$attrs['index']) $attrs['index'] = 'null'; - if($attrs['type']!='body') $attrs['type'] = 'head'; - - // if target ends with lang, load language pack - if(substr($target, -4)=='lang') { - if(substr($target,0,2)=='./') $target = substr($target, 2); - $lang_dir = $base_path.$target; - if(is_dir($lang_dir)) $output = sprintf('', $lang_dir); - - // otherwise try to load xml, css, js file - } else { - if(substr($target,0,1)!='/') $source_filename = $base_path.$target; - else $source_filename = $target; - $source_filename = str_replace(array('/./','//'),'/',$source_filename); - - // get filename and path - $tmp_arr = explode("/",$source_filename); - $filename = array_pop($tmp_arr); - - //$base_path = implode("/",$tmp_arr)."/"; - - // get the ext - $tmp_arr = explode(".",$filename); - $ext = strtolower(array_pop($tmp_arr)); - - $output = ' $val) - { - $output .= '$_load_attrs[\''.$key.'\'] = \'' . preg_replace('/\{([^@^ ][^\{\}\n]+)\}/i', "'.\\1.'", $val) . '\';'; - } - $output .= '?>'; - - // according to ext., import the file - switch($ext) { - // xml js filter - case 'xml' : - if(preg_match('/^(http|https)/i',$source_filename)) return; - // create an instance of XmlJSFilter class, then create js and handle Context::addJsFile - $output .= sprintf( - 'compile();%s'. - '?>%s', - "\n", - "\n", - dirname($base_path . $attrs['target']).'/', - $filename, - "\n", - "\n", - "\n" - ); - break; - // css file - case 'css' : - if($type == 'unload') { - $output = sprintf("", $source_filename, $attrs['targetie'], $attrs['media']); - } else { - $meta_file = $source_filename; - $output .= ''; - } - break; - // js file - case 'js' : - if($type == 'unload') { - $output = sprintf("", $source_filename, $attrs['targetie']); - } else { - $meta_file = $source_filename; - $output .= ''; - } - break; + if($idx !== false) { + while($dirs[$idx] && $dirs[$idx] === $paths[0]) { + array_splice($dirs, $idx, 1); + array_shift($paths); + } + $path = strtr(realpath($fileDir.'/'.implode('/', $paths)),'\\','/'); } } - $output .= ''; - if($meta_file) $output = ''.$output; - return $output; + $path = preg_replace('/^'.preg_quote(_XE_PATH_,'/').'/', '', $path); + + return $path; } /** * @brief replace PHP variables of $ character **/ - function _replaceVarInPHP($buff) { - $head = $tail = ''; - while(false !== $pos = strpos($buff, ''); - $body = substr($buff,0,$pos+2); - $head .= preg_replace_callback('/(.?)\$(\w+[a-z0-9\_\-\[\]\'\"]+)/is',array($this, '_replaceVarString'), $body); - - $buff = substr($buff,$pos+2); - } - return $head.$buff; + function _replaceVar($php) { + if(!$php) return ''; + return preg_replace('@(?$1', $php); } - - - /** - * @brief if class::$variable_name in php5, replace the function not to use context - **/ - function _replaceVarString($matches) - { - if($matches[1]==':') return $matches[0]; - if(substr($matches[2],0,1)=='_') return $matches[0]; - return $matches[1].'$__Context->'.$matches[2]; - } - - /** - * @brief replace with php code - * @param[in] $matches match - * @return replaced result - **/ - function _compileIncludeToCode($matches) { - // if target string to include contains variables handle them - $arg = str_replace(array('"','\''), '', $matches[1]); - if(!$arg) return; - - $tmp_arr = explode("/", $arg); - for($i=0;$ifile), $arg); - $path = substr($this->path,-1)=='/'?substr($this->path,0,-1):$this->path; - $source_filename = sprintf("%s/%s", $path, $arg); - - // step2: check path from root - if(!file_exists($source_filename)) $source_filename = './'.$arg; - if(!file_exists($source_filename)) return; - - // split into path and filename - $tmp_arr = explode('/', $source_filename); - $filename = array_pop($tmp_arr); - $path = implode('/', $tmp_arr).'/'; - - // try to include - $output = sprintf( - 'compile(\'%s\',\'%s\');%s'. - '?>%s', - "\n", - "\n", - $path, $filename, "\n", - "\n" - ); - - return $output; - } - - /** - * @brief replace $... variables in { } with Context::get(...) - * @param[in] $matches match - * @return replaced result in case of success or NULL in case of error - **/ - function _compileVarToContext($matches) { - $str = trim(substr($matches[0],1,strlen($matches[0])-2)); - if(!$str) return $matches[0]; - if(!in_array(substr($str,0,1),array('(','$','\'','"'))) { - if(preg_match('/^([^\( \.]+)(\(| \.)/i',$str,$m)) { - $func = trim($m[1]); - if(strpos($func,'::')===false) { - if(!function_exists($func)) { - return $matches[0]; - } - } else { - list($class, $method) = explode('::',$func); - // FIXME regardless of whether class/func name is case-sensitive, it is safe - // to assume names are case sensitive. We don't have compare twice. - if(!class_exists($class) || !in_array($method, get_class_methods($class))) { - // In some environment, the name of classes and methods may be case-sensitive - list($class, $method) = explode('::',strtolower($func)); - if(!class_exists($class) || !in_array($method, get_class_methods($class))) { - return $matches[0]; - } - } - } - } else { - if(!defined($str)) return $matches[0]; - } - } - return ']+)/i','$__Context->\\1', $str).');?>'; - } - - /** - * @brief replace @... function in { } into print func(..) - * @param[in] $matches match - * @return replaced result - **/ - function _compileVarToSilenceExecute($matches) { - if(strtolower(trim(str_replace(array(';',' '),'', $matches[1])))=='return') return ''; - return ']+)/i','$__Context->\\1', trim($matches[1])).';?>'; - } - - /** - * @brief replace code in with php code - * @param[in] $matches match - * @return changed result - **/ - function _compileFuncToCode($matches) { - static $idx = 0; - $code = trim($matches[1]); - if(!$code) return; - - switch(strtolower($code)) { - case 'else' : - $output = '}else{'; - break; - case 'end' : - case 'endif' : - case 'endfor' : - case 'endforeach' : - case 'endswitch' : - $output = '}'; - break; - case 'break' : - $output = 'break;'; - break; - case 'default' : - $output = 'default :'; - break; - case 'break@default' : - $output = 'break; default :'; - break; - default : - $suffix = '{'; - - if(substr($code, 0, 4) == 'else') { - $code = '}'.$code; - } elseif(substr($code, 0, 7) == 'foreach') { - $tmp_str = substr($code, 8); - $tmp_arr = explode(' ', $tmp_str); - $var_name = $tmp_arr[0]; - $prefix = '$Context->__idx['.$idx.']=0;'; - if(substr($var_name, 0, 1) == '$') { - $prefix .= sprintf('if(count($__Context->%s)) ', substr($var_name, 1)); - } else { - $prefix .= sprintf('if(count(%s)) ', $var_name); - } - $idx++; - $suffix .= '$__idx['.$idx.']=($__idx['.$idx.']+1)%2; $cycle_idx = $__idx['.$idx.']+1;'; - } elseif(substr($code, 0, 4) == 'case') { - $suffix = ':'; - } elseif(substr($code, 0, 10) == 'break@case') { - $code = 'break; case'.substr($code, 10); - $suffix = ':'; - } - $output = preg_replace('/\$([a-zA-Z0-9\_\-]+)/i', '$__Context->\\1', $code.$suffix); - break; - } - - return sprintf('', $prefix, $output); - } - - - /** - * @brief replace xe specific code, "" with appropriate php code - * @param[in] $matches match - * @return Returns modified result or NULL in case of error - **/ - function _compileImportCode($matches) { - // find xml file - $base_path = $this->path; - $given_file = trim($matches[1]); - if(!$given_file) return; - if(isset($matches[3])) $optimized = strtolower(trim($matches[3])); - if(!$optimized) $optimized = 'true'; - if(isset($matches[5])) $media = trim($matches[5]); - if(!$media) $media = 'all'; - if(isset($matches[7])) $targetie = trim($matches[7]); - if(!$targetie) $targetie = ''; - else $optimized = 'false'; - - if(isset($matches[9])) $index = intval($matches[9]); - if(!$index) $index = 'null'; - if(isset($matches[11])) $type = strtolower(trim($matches[11])); - if($type!='body') $type = 'head'; - - // if given_file ends with lang, load language pack - if(substr($given_file, -4)=='lang') { - if(substr($given_file,0,2)=='./') $given_file = substr($given_file, 2); - $lang_dir = $base_path.$given_file; - if(is_dir($lang_dir)) $output = sprintf('', $lang_dir); - - // otherwise try to load xml, css, js file - } else { - if(preg_match('/^(http|https):/i',$given_file)) $source_filename = $given_file; - elseif(substr($given_file,0,1)!='/') $source_filename = sprintf("%s%s",$base_path, $given_file); - else $source_filename = $given_file; - - // get filename and path - $tmp_arr = explode("/",$source_filename); - $filename = array_pop($tmp_arr); - - $base_path = implode("/",$tmp_arr)."/"; - - // get the ext - $tmp_arr = explode(".",$filename); - $ext = strtolower(array_pop($tmp_arr)); - - // according to ext., import the file - switch($ext) { - // xml js filter - case 'xml' : - // create an instance of XmlJSFilter class, then create js and handle Context::addJsFile - $output = sprintf( - 'compile();%s'. - '?>%s', - "\n", - "\n", - $base_path, - $filename, - "\n", - "\n", - "\n" - ); - break; - // css file - case 'css' : - if(preg_match('/^(http|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, $media, $targetie, $index); - } else { - $meta_file = $base_path.$filename; - $output = sprintf('', $base_path, $filename, $media, $targetie, $index); - } - break; - // js file - case 'js' : - if(preg_match('/^(http|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, $type, $targetie, $index); - } else { - $meta_file = $base_path.$filename; - $output = sprintf('', $base_path, $filename, $type, $targetie, $index); - } - break; - } - } - - if($meta_file) $output = ''.$output; - return $output; - } - - /** - * @brief import javascript plugin - * @param[in] $matches match - * @return result loading the plugin - * @remarks javascript plugin works as optimized = false - **/ - function _compileLoadJavascriptPlugin($matches) { - $base_path = $this->path; - $plugin = trim($matches[1]); - return sprintf('', $plugin); - } - - /** - * @brief remove loading part of css/ js file - * @param[in] $matches match - * @return removed result - **/ - function _compileUnloadCode($matches) { - // find xml file - $base_path = $this->path; - $given_file = trim($matches[1]); - if(!$given_file) return; - if(isset($matches[3])) $optimized = strtolower(trim($matches[3])); - if(!$optimized) $optimized = 'true'; - if(isset($matches[5])) $media = trim($matches[5]); - if(!$media) $media = 'all'; - if(isset($matches[7])) $targetie = trim($matches[7]); - if(!$targetie) $targetie = ''; - else $optimized = 'false'; - - if(substr($given_file,0,1)!='/') $source_filename = sprintf("%s%s",$base_path, $given_file); - else $source_filename = $given_file; - - // get path and file nam - $tmp_arr = explode("/",$source_filename); - $filename = array_pop($tmp_arr); - - $base_path = implode("/",$tmp_arr)."/"; - - // get an ext. - $tmp_arr = explode(".",$filename); - $ext = strtolower(array_pop($tmp_arr)); - - switch($ext) { - // css file - case 'css' : - if(preg_match('/^(http|https|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, $targetie, $media); - } else { - $output = sprintf('', $base_path, $filename, $targetie, $media); - } - break; - // js file - case 'js' : - if(preg_match('/^(http|https|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, $targetie); - } else { - $output = sprintf('', $base_path, $filename, $targetie); - } - break; - } - - return $output; - } } ?>