Params);\n"; $this->Buffers[0] = '\n"; // finding all the tags $reg = '(.*?)(<[\\/]?)inp2:([^>]*?)([\\/]?>(\r\n){0,1})'; preg_match_all('/'.$reg.'/s', $data, $results, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); $this->InsideComment = false; foreach ($results as $tag_data) { $tag = array( 'opening' => $tag_data[2][0], 'tag' => $tag_data[3][0], 'closing' => $tag_data[4][0], 'line' => substr_count(substr($data, 0, $tag_data[2][1]), "\n")+1, 'file' => $pre_parsed['tname'], ); // the idea is to count number of comment openings and closings before current tag // if the numbers do not match we inverse the status of InsideComment if (substr_count($tag_data[1][0], '')) { $this->InsideComment = !$this->InsideComment; } // appending any text/html data found before tag $this->Buffers[$this->Level] .= $tag_data[1][0]; if (!$this->InsideComment) { $this->ProcessTag($tag); } else { $this->Buffers[$this->Level] .= $tag_data[2][0].$tag_data[3][0].$tag_data[4][0]; } } if ($this->Level > 0) { $this->Application->handleError(E_USER_ERROR, 'Unclosed tag opened by '.$this->TagInfo($this->Stack[$this->Level]->Tag), $this->Stack[$this->Level]->Tag['file'], $this->Stack[$this->Level]->Tag['line']); } // appending text data after last tag (after its closing pos), // if no tag was found at all ($tag_data is not set) - append the whole $data $this->Buffers[$this->Level] .= isset($tag_data) ? substr($data, $tag_data[4][1]+strlen($tag_data[4][0])) : $data; // saving compiled version $compiled = fopen($pre_parsed['fname'], 'w'); if (!fwrite($compiled, $this->Buffers[0])) { trigger_error('Saving compiled template failed', E_USER_ERROR); } fclose($compiled); } function SplitParamsStr($params_str) { preg_match_all('/([\${}a-zA-Z0-9_.\\-\\\\#\\[\\]]+)=(["\']{1,1})(.*?)(? $val){ $values[$val[1]] = str_replace('\\' . $val[2], $val[2], $val[3]); } return $values; } function SplitTag($tag) { if (!preg_match('/([^_ \t\n]*)[_]?([^ \t\n]*)[ \t\n]*(.*)$$/s', $tag['tag'], $parts)) { // this is virtually impossible, but just in case $this->Application->handleError(E_USER_ERROR, 'Incorrect tag format: '.$tag['tag'], $tag['file'], $tag['line']); } $splited['prefix'] = $parts[2] ? $parts[1] : '__auto__'; $splited['name'] = $parts[2] ? $parts[2] : $parts[1]; $splited['attrs'] = $parts[3]; return $splited; } function ProcessTag($tag) { $tag = array_merge($tag, $this->SplitTag($tag)); $tag['processed'] = false; $tag['NP'] = $this->SplitParamsStr($tag['attrs']); $o = ''; $tag['is_closing'] = $tag['opening'] == ''; if (class_exists('_Tag_'.$tag['name'])) { // block tags should have special handling class if ($tag['opening'] == '<') { $class = '_Tag_'.$tag['name']; $instance = new $class($tag); $instance->Parser =& $this; /* @var $instance _BlockTag */ $this->Stack[++$this->Level] =& $instance; $this->Buffers[$this->Level] = ''; $o .= $instance->Open($tag); } if ($tag['is_closing']) { // not ELSE here, because tag may be and still has a handler-class if ($this->Level == 0) { $dump = array(); foreach ($this->Stack as $instance) { $dump[] = $instance->Tag; } print_pre($dump); $this->Application->handleError(E_USER_ERROR, 'Closing tag without an opening: '.$this->TagInfo($tag).' - probably opening tag was removed or nested tags error', $tag['file'], $tag['line']); } if ($this->Stack[$this->Level]->Tag['name'] != $tag['name']) { $opening_tag = $this->Stack[$this->Level]->Tag; $this->Application->handleError(E_USER_ERROR, 'Closing tag '.$this->TagInfo($tag).' does not match opening tag at current nesting level ('.$this->TagInfo($opening_tag).' opened at line '.$opening_tag['line'].')', $tag['file'], $tag['line']); } $o .= $this->Stack[$this->Level]->Close($tag); // DO NOT use $this->Level-- here because it's used inside Close $this->Level--; } } else { // regular tags - just compile if (!$tag['is_closing']) { $this->Application->handleError(E_USER_ERROR, 'Tag without a handler: '.$this->TagInfo($tag).' - probably missing <empty /> tag closing', $tag['file'], $tag['line']); } if ($this->Level > 0) $o .= $this->Stack[$this->Level]->PassThrough($tag); if (!$tag['processed']) { $o .= 'CompileTag($tag)." ?>\n"; } } $this->Buffers[$this->Level] .= $o; } function TagInfo($tag, $with_params=false) { return "{$tag['prefix']}_{$tag['name']}".($with_params ? ' '.$tag['attrs'] : '').""; } function CompileParamsArray($arr) { $to_pass = 'Array('; foreach ($arr as $name => $val) { $to_pass .= '"'.$name.'" => "'.str_replace('"', '\"', $val).'",'; } $to_pass .= ')'; return $to_pass; } function CompileTag($tag) { $to_pass = $this->CompileParamsArray($tag['NP']); $code = ''; if ($tag['prefix'] == '__auto__') { $prefix = $this->GetParam('PrefixSpecial'); $code .= '$_p_ =& $_parser->GetProcessor($PrefixSpecial);'."\n"; $code .= 'echo $_p_->ProcessParsedTag(\''.$tag['name'].'\', '.$to_pass.', "$PrefixSpecial", \''.$tag['file'].'\', '.$tag['line'].');'."\n"; } else { $prefix = $tag['prefix']; $code .= '$_p_ =& $_parser->GetProcessor("'.$tag['prefix'].'");'."\n"; $code .= 'echo $_p_->ProcessParsedTag(\''.$tag['name'].'\', '.$to_pass.', "'.$tag['prefix'].'", \''.$tag['file'].'\', '.$tag['line'].');'."\n"; } if (isset($tag['NP']['result_to_var'])) { $code .= "\$params['{$tag['NP']['result_to_var']}'] = \$_parser->GetParam('{$tag['NP']['result_to_var']}');\n"; $code .= "\${$tag['NP']['result_to_var']} = \$params['{$tag['NP']['result_to_var']}'];\n"; } if ($prefix && strpos($prefix, '$') === false) { $p =& $this->GetProcessor($prefix); if (!$p->CheckTag($tag['name'], $tag['prefix'])) { $this->Application->handleError(E_USER_ERROR, 'Unknown tag: '.$this->TagInfo($tag).' - incorrect tag name or prefix ', $tag['file'], $tag['line']); } } return $code; } function CheckTemplate($t, $silent=null) { $pre_parsed = $this->Application->TemplatesCache->GetPreParsed($t); if (!$pre_parsed) { if (!$silent) { if ($this->Application->isDebugMode()) $this->Application->Debugger->appendTrace(); trigger_error("Cannot include $t - file does not exist", E_USER_ERROR); } return false; } if (!$pre_parsed || !$pre_parsed['active'] || defined('DBG_NPARSER_FORCE_COMPILE') && DBG_NPARSER_FORCE_COMPILE) { $inc_parser =& new NParser(); $inc_parser->Compile($pre_parsed); } return $pre_parsed; } function Run($t, $silent=null) { $pre_parsed = $this->CheckTemplate($t, $silent); if (!$pre_parsed) return false; ob_start(); $_parser =& $this; if ($pre_parsed['mode'] == 'file') { include($pre_parsed['fname']); } else { eval('?'.'>'.$pre_parsed['content']); } $output = ob_get_contents(); ob_end_clean(); return $output; } function &GetProcessor($prefix) { static $Processors = array(); if (!isset($Processors[$prefix])) { $Processors[$prefix] = $this->Application->recallObject($prefix.'_TagProcessor'); } return $Processors[$prefix]; } function SelectParam($params, $possible_names) { if (!is_array($params)) return; if (!is_array($possible_names)) $possible_names = explode(',', $possible_names); foreach ($possible_names as $name) { if( isset($params[$name]) ) return $params[$name]; } return false; } function SetParams($params) { $this->Params = $params; $keys = array_keys($this->Params); } function GetParam($name) { return isset($this->Params[$name]) ? $this->Params[$name] : false; } function SetParam($name, $value) { $this->Params[$name] = $value; } function PushParams($params) { $this->ParamsStack[$this->ParamsLevel++] = $this->Params; $this->Params = $params; } function PopParams() { $this->Params = $this->ParamsStack[--$this->ParamsLevel]; } function ParseBlock($params, $pass_params=false) { if ($pass_params || isset($params['pass_params'])) $params = array_merge($this->Params, $params); $this->PushParams($params); if (!isset($this->Elements[$params['name']])) { $pre_parsed = $this->Application->TemplatesCache->GetPreParsed($params['name']); if ($pre_parsed) { return $this->IncludeTemplate($params); } if ($this->Application->isDebugMode()) { $this->Application->Debugger->appendTrace(); } $trace_results = debug_backtrace(); $this->Application->handleError(E_USER_ERROR, 'Rendering of undefined element '.$params['name'].'', $trace_results[0]['file'], $trace_results[0]['line']); } $m_processor =& $this->GetProcessor('m'); $flag_values = $m_processor->PreparePostProcess($params); $f_name = $this->Elements[$params['name']]; $ret = $f_name($this, $params); $ret = $m_processor->PostProcess($ret, $flag_values); $this->PopParams(); return $ret; } function IncludeTemplate($params, $silent=null) { $t = is_array($params) ? $this->SelectParam($params, 't,template,block,name') : $params; $t = eregi_replace("\.tpl$", '', $t); $this->PushParams($params); $ret = $this->Run($t, $silent); $this->PopParams(); return $ret; } }