From f8237d2a3a6ef7183bea101fe0b40ccf6d8fca05 Mon Sep 17 00:00:00 2001 From: ovclas Date: Mon, 26 Sep 2011 06:43:55 +0000 Subject: [PATCH] issue 160 rollback TemplateHandler to r9106 git-svn-id: http://xe-core.googlecode.com/svn/branches/1.5.0@9329 201d5d3c-b55e-5fd7-737f-ddc643e51545 --- classes/template/TemplateHandler.class.php | 977 +++++++++++++++------ 1 file changed, 714 insertions(+), 263 deletions(-) diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php index 78dc5ec15..0859e3079 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 { + class TemplateHandler extends Handler { var $compiled_path = './files/cache/template_compiled/'; ///< path of compiled caches files @@ -18,37 +18,30 @@ 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() - { - $this->xe_path = rtrim(preg_replace('/([^\.^\/]+)\.php$/i','',$_SERVER['SCRIPT_NAME']),'/'); - } - /** * @brief returns TemplateHandler's singleton object * @return TemplateHandler instance **/ - function &getInstance() - { - static $oTemplate = null; - + function &getInstance() { if(__DEBUG__==3 ) { if(!isset($GLOBALS['__TemplateHandlerCalled__'])) $GLOBALS['__TemplateHandlerCalled__']=1; else $GLOBALS['__TemplateHandlerCalled__']++; } - if(!$oTemplate) $oTemplate = new TemplateHandler(); - - return $oTemplate; + if(!$GLOBALS['__TemplateHandler__']) { + $GLOBALS['__TemplateHandler__'] = new TemplateHandler(); + } + return $GLOBALS['__TemplateHandler__']; } /** * @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'; @@ -57,18 +50,22 @@ 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->web_path = $this->xe_path.'/'.ltrim(preg_replace('@^'.preg_quote(_XE_PATH_,'@').'|\./@','',$this->path),'/'); + $this->xe_path = preg_replace('/([^\.^\/]+)\.php$/i','',$_SERVER['SCRIPT_NAME']); + $this->web_path = $this->xe_path.str_replace(_XE_PATH_,'',$this->path); // get compiled file name - $hash = md5($this->file . __ZBXE_VERSION__); - $this->compiled_file = "{$this->compiled_path}{$hash}.compiled.php"; + $this->compiled_file = sprintf('%s%s.compiled.php',$this->compiled_path, md5($this->file . __ZBXE_VERSION__)); // compare various file's modified time for check changed - $this->handler_mtime = filemtime(__FILE__); + $this->handler_mtime = filemtime(_XE_PATH_.'classes/template/TemplateHandler.class.php'); + + $this->buff = null; } /** @@ -78,9 +75,7 @@ * @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='') { - $buff = ''; - + function compile($tpl_path, $tpl_filename, $tpl_file = '') { // store the starting time for debug information if(__DEBUG__==3 ) $start = getMicroTime(); @@ -88,31 +83,31 @@ $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."; + if(!$this->file || !file_exists($this->file)) return sprintf('Err : "%s" template file does not exists.', $this->file); $source_template_mtime = filemtime($this->file); $latest_mtime = $source_template_mtime>$this->handler_mtime?$source_template_mtime:$this->handler_mtime; - // cache control + // cache controll $oCacheHandler = &CacheHandler::getInstance('template'); // get cached buff if($oCacheHandler->isSupport()){ $cache_key = 'template:'.$this->file; - $buff = $oCacheHandler->get($cache_key, $latest_mtime); + $this->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(file_exists($this->compiled_file) && filemtime($this->compiled_file)>$latest_mtime) { + $this->buff = FileHandler::readFile($this->compiled_file); } } - if(!$buff) { - $buff = $this->parse(); - if($oCacheHandler->isSupport()) $oCacheHandler->put($cache_key, $buff); - else FileHandler::writeFile($this->compiled_file, $buff); + if(!$this->buff) { + $this->parse(); + if($oCacheHandler->isSupport()) $oCacheHandler->put($cache_key, $this->buff); + else FileHandler::writeFile($this->compiled_file, $this->buff); } - $output = $this->_fetch($buff); + $output = $this->_fetch(); // store the ending time for debug information if(__DEBUG__==3 ) $GLOBALS['__template_elapsed__'] += getMicroTime() - $start; @@ -132,10 +127,12 @@ // if target file does not exist exit if(!$this->file || !file_exists($this->file)) { Context::close(); - exit("Cannot find the template file: '{$this->file}'"); + printf('"%s" template file is not exists.', $this->file); + exit(); } - return $this->parse(); + $this->parse(); + return $this->buff; } /** @@ -145,33 +142,65 @@ * @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($buff=null) { - if(is_null($buff)) { - if(!is_readable($this->file)) return; + function parse() { + if(!file_exists($this->file)) return; - // read tpl file - $buff = FileHandler::readFile($this->file); - } + // read tpl file + $buff = FileHandler::readFile($this->file); // replace value of src in img/input/script tag - $buff = preg_replace_callback('/<(?:img|input|script)(?:(?!["\'\/]\s*>).)* src="(?!https?:\/\/|[\/\{])([^"]+)"/is', array($this, '_replacePath'), $buff); + $buff = preg_replace_callback('/<(img|input|script)([^>]*)src="([^"]*?)"/is', array($this, '_replacePath'), $buff); - // replace loop and cond template syntax - $buff = $this->_parseInline($buff); + // replace the loop template syntax + $buff = $this->_replaceLoop($buff); - // include, unload/load, import - $buff = preg_replace_callback('/<(include|(?:un)?load)( .+?)>|||{@(.+?)}|{([^@ \\\\][^\n\r\{\}]+?)}/s', array($this, '_parseResource'), $buff); + // |replace the cond template syntax + $buff = $this->_replaceCond($buff); - // remove block which is a virtual tag and remove comments - $buff = preg_replace('@|\s?@is','',$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); // form auto generation $buff = preg_replace_callback('/(|[^<>]+)*?>)(.*?)(<\/form>)/is', array($this, '_compileFormAuthGeneration'), $buff); - // prevent from calling directly before writing into file - $buff = ''.$buff; + // replace variables + $buff = preg_replace_callback('/\{[^@^ ]([^\{\}\n]+)\}/i', array($this, '_compileVarToContext'), $buff); - return $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; } /** @@ -214,14 +243,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); @@ -234,24 +263,17 @@ * @param[in] $tpl_path set context's tpl path * @return result string **/ - function _fetch($buff) { - if(!$buff) return; + function _fetch() { + if(!$this->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(); - if(substr($buff, 0, 7) == 'file://') { - include substr($buff, 7); - } else { - $eval_str = "?>".$buff; - eval($eval_str); - } - + $eval_str = "?>".$this->buff; + eval($eval_str); return ob_get_clean(); } @@ -263,244 +285,673 @@ **/ function _replacePath($matches) { - $src = preg_replace('@^(\./)+@', '', trim($matches[1])); - $src = $this->web_path.$src; - $src = str_replace('/./', '/', $src); - - while(($tmp=preg_replace('@[^/]+/\.\./@', '', $src))!==$src) $src = $tmp; - - return substr($matches[0],0,-strlen($matches[1])-6)."src=\"{$src}\""; + 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]; } - function _parseInline($buff) + /** + * @brief replace loop syntax + **/ + function _replaceLoop($buff) { - if(preg_match_all('/<([a-zA-Z0-9]+)[^>]*(?:(?:|{[^}]*?})[^>]*)*?(?:[ \|]cond| loop)="/s', $buff, $matches) === false) return $buff; + while(false !== $pos = strpos($buff, ' loop="')) + { + $pre = substr($buff,0,$pos); + $next = substr($buff,$pos); - $tags = '(?:'.implode('|',array_unique($matches[1])).')'; - $split_regex = '@(<(?:/?'.$tags.'|'.$tags.'.*?["\'/]\s*)>)@s'; + $pre_pos = strrpos($pre, '<'); - $nodes = preg_split($split_regex, $buff, -1, PREG_SPLIT_DELIM_CAPTURE); + preg_match('/^ loop="([^"]+)"/i',$next,$m); + $tag = substr($next,0,strlen($m[0])); + $next = substr($next,strlen($m[0])); + $next_pos = strpos($next, '<'); - // list of self closing tags - $self_closing = explode(',', 'area,base,basefont,br,hr,input,img,link,meta,param,frame,col'); + $tag = substr($pre, $pre_pos). $tag. substr($next, 0, $next_pos); + $pre = substr($pre, 0, $pre_pos); + $next = substr($next, $next_pos); - for($idx=1,$node_len=count($nodes); $idx < $node_len; $idx+=2) { - if(!($node=$nodes[$idx])) continue; + $tag_name = trim(substr($tag,1,strpos($tag,' '))); + $tag_head = $tag_tail = ''; - if(preg_match_all('@\s(loop|cond)="([^"]+)"@', $node, $matches)) { - $closing = 0; + if(!preg_match('/ loop="([^"]+)"/is',$tag)) { + print "Invalid XpressEngine Template Syntax
"; + print "File : ".$this->file."
"; + print "Code : ".htmlspecialchars($tag); + exit(); + } - // process opening tag - foreach($matches[1] as $n=>$stmt) { - $expr = $matches[2][$n]; - $expr = $this->_replaceVar($expr); - $closing++; + preg_match_all('/ loop="([^"]+)"/is',$tag,$m); + $tag = preg_replace('/ loop="([^"]+)"/is','', $tag); - 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; + 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 .= ''; } } - $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; - } - } + elseif(false!==strpos($loop,';')) + { + $tag_head .= ''; + $tag_tail .= ''; + } + else + { + $t = explode('=',$loop); + if(count($t)==2) + { + $tag_head .= ''; + $tag_tail .= ''; } } } - if(strpos($node, '|cond="') !== false) { - $node = preg_replace('@(\s[\w:]+="[^"]+?")\|cond="(.+?)"@s', '$1', $node); - $node = $this->_replaceVar($node); + 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,' %s="%s"', $m[3], $m[1], $m[2]), $matches[0]); + } } - $buff = implode('', $nodes); + 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,'_replaceVar($m[9]).' ?>'; + if(!preg_match('/target=\"([^\"]+)\"/is',$matches[0], $m)) { + print '"target" attribute missing in "'.htmlspecialchars($matches[0]); + exit(); } - // {@ ... } - if($m[8]) + $target = $m[1]; + if(substr($target,0,1)=='/') { - return '_replaceVar($m[8]).' ?>'; + $target = substr($target,1); + $pos = strrpos('/',$target); + $filename = substr($target,$pos+1); + $path = substr($target,0,$pos); + } else { + if(substr($target,0,2)=='./') $target = substr($target,2); + $pos = strrpos('/',$target); + $filename = substr($target,$pos); + $path = $this->path.substr($target,0,$pos); } - // or or or - if($m[1]=='load'||$m[1]=='unload'||$m[3]=='%import'||$m[3]=='%unload') - { - $attr = array(); - if($m[1]) { - if(!preg_match_all('@ (\w+)="([^"]+)"@', $m[2], $mm)) return $m[0]; - foreach($mm[1] as $idx=>$name) { - $attr[$name] = $mm[2][$idx]; - } - $cmd = $m[1]; - } else { - if(preg_match_all('@,(\w+)="([^"]+)"@', $m[5], $mm)) { - foreach($mm[1] as $idx=>$name) { - $attr[$name] = $mm[2][$idx]; - } - } - $attr['target'] = $m[4]; - $cmd = substr($m[3], 1); - if($cmd == 'import') $cmd = 'load'; - } - - $metafile = ''; - $pathinfo = pathinfo($attr['target']); - - $isRemoteFile = !!preg_match('@^https?://@i', $attr['target']); - - if(!$isRemoteFile) { - 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($isRemoteFile || $cmd != 'load') 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($cmd == 'load') { - $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);"; - } else { - $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}');"; - } - break; - case 'css': - if($cmd == 'load') { - $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);"; - } else { - $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');"; - } - break; - } - - $result = ""; - if($metafile) $result = "".$result; - - return $result; - } - - // or - if($m[1]=='include' || $m[3]=='#include') - { - if(!$this->file) return ''; - if($m[1]) { - if(!preg_match('@target="(.+?)"@', $m[2], $mm)) return ''; - $file = $mm[1]; - } else { - $file = $m[4]; - } - $pathinfo = pathinfo($file); - $fileDir = $this->_getRelativeDir($pathinfo['dirname']); - - if(!$fileDir) return ''; - - return "compile('{$fileDir}','{$pathinfo['basename']}') ?>"; - } - - // - if($m[3]=='%load_js_plugin') - { - $plugin = $this->_replaceVar($m[4]); - if(strpos($plugin, '$__Context') === false) $plugin = "'{$plugin}'"; - return ""; - } - - // such as , , - if($m[6]) - { - if(!preg_match('/^(?:(if|switch|for|foreach|while)|(end(?:if|switch|for|foreach|while)?)|(else(?:if)?)|(break@)?(case|default)|(break))$/', $m[6], $mm)) return ''; - if($mm[1]) { - $precheck = ''; - if($mm[1] == 'foreach') { - $var = preg_replace('/^\s*\(\s*(.+?) .*$/', '$1', $m[7]); - $precheck = "if({$var}&&count({$var}))"; - } - return '_replaceVar($precheck.$m[6].$m[7]).'{ ?>'; - } - if($mm[2]) return ""; - if($mm[3]) return "_replaceVar($m[7])."{ ?>"; - if($mm[5]) return ""; - if($mm[6]) return ""; - return ''; - } - - return $m[0]; + return sprintf( + 'compile(\'%s\',\'%s\');%s'. + '?>%s', + "\n", + "\n", + $path, $filename, "\n", + "\n" + ); } - function _getRelativeDir($path) - { - $fileDir = dirname(strtr(realpath($this->file),'\\','/')); - if($path{0} != '/') $path = strtr(realpath($fileDir.'/'.$path),'\\','/'); + /** + * @brief replace load tags + **/ + function _replaceLoad($matches) { + $output = $matches[0]; + if(!preg_match_all('/ ([^=]+)=\"([^\"]+)\"/is',$matches[0], $m)) return $matches[0]; - $path = preg_replace('/^'.preg_quote(_XE_PATH_,'/').'/', '', $path); - $path = ltrim($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]); + } - return $path; + if(!$attrs['target']) return $matches[0]; + + $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($meta_file) $output = ''.$output; + return $output; } /** * @brief replace PHP variables of $ character **/ - function _replaceVar($php) { - if(!$php) return ''; - return preg_replace('@(?$1', $php); + 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; } + + + /** + * @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; + } } ?>