templateDir = BSFunctions::FetchSourcePath($dir); } // ################################################################### /** * Sets the file extension for the templates * * @param string File extension */ public function setExtension($ext) { $this->extension = $ext; } // ################################################################### /** * Sets the name of the table to access for the datbase cache * * @param string DB table name */ public function setDatabaseCache($table) { $this->dbCacheTable = $table; } // ################################################################### /** * Sets the pre-parse hook method which is called before any other * processing is done on the template. * * @param string Method name */ public function setPreParseHook($hook) { $this->preParseHook = $hook; } // ################################################################### /** * Takes an array of template names, loads them, and then stores a * parsed version for optimum speed. * * @param array List of template names to be cached */ public function cache($namearray) { if (sizeof($this->cache) > 0) { trigger_error('You cannot cache templates more than once per initialization'); } else { $dbCache = array(); if ($this->dbCacheTable) { $db =& BSRegister::GetType('Db'); $cache = $db->query("SELECT * FROM {$this->dbCacheTable} WHERE filename IN ('" . implode("', '", $namearray) . "')"); while ($tpl = $db->fetchArray($cache)) { $time = filemtime(BSRegister::GetAppPath() . $this->templateDir . $tpl['filename'] . '.' . $this->extension); $template = $tpl['template']; if ($time > $tpl['timestamp']) { $template = $this->_parseTemplate($this->_loadTemplate($tpl['filename'])); $db->query("UPDATE {$this->dbCacheTable} SET template = '" . $db->escapeString($template) . "', timestamp = " . TIMENOW . " WHERE filename = '" . $tpl['filename'] . "'"); $tpl['template'] = $template; } $dbCache["$tpl[filename]"] = $template; } } foreach ($namearray AS $name) { if ($this->dbCacheTable) { if (isset($dbCache["$name"])) { $template = $dbCache["$name"]; } else { $template = $this->_parseTemplate($this->_loadTemplate($name)); $db->query("INSERT INTO {$this->dbCacheTable} (filename, template, timestamp) VALUES ('$name', '" . $db->escapeString($template) . "', " . TIMENOW . ")"); } } else { $template = $this->_parseTemplate($this->_loadTemplate($name)); } $this->cache["$name"] = $template; $this->usage["$name"] = 0; } } } // ################################################################### /** * Loads a template from the cache or the _load function and stores the * parsed version of it * * @param string The name of the template * * @return string A parsed and loaded template */ public function fetch($name) { if (isset($this->cache["$name"])) { $template = $this->cache["$name"]; } else { $this->uncached[] = $name; BSRegister::Debug("Manually loading template '$name'"); $template = $this->_loadTemplate($name); $template = $this->_parseTemplate($template); } if (!isset($this->usage["$name"])) { $this->usage["$name"] = 0; } $this->usage["$name"]++; return $template; } // ################################################################### /** * Output a template fully compiled to the browser * * @param string Compiled and ready template */ public function flush($template) { ob_start(); if (empty($template)) { trigger_error('There was no output to print'); exit; } if ($this->doneflush) { trigger_error('A template has already been sent to the output buffer'); exit; } $debugBlock = ''; if (BSRegister::GetDebug()) { if (defined('SVN') AND preg_match('#^\$Id:?#', constant('SVN'))) { $debugBlock .= preg_replace('#\$' . 'Id: (.+?) ([0-9].+?) [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}(.+?) (.+?) \$#', "\n
\n" . '
\1 — r\2
', constant('SVN')); } if (defined('ISSO_MT_START')) { $debugBlock .= "\n
Executed in " . round(BSFunctions::FetchMicrotimeDiff(ISSO_MT_START), 10) . ' seconds
'; } $debugBlock .= "\n
" . BSRegister::GetDebugList() . "
"; $optlist = array(); $usage = array(); foreach ($this->usage AS $name => $count) { if (in_array($name, $this->uncached)) { $optlist[] = $name . '[' . $count . ']'; } $usage[] = $name . " ($count)"; } $sizeof = sizeof($this->uncached); if ($sizeof > 0) { $debugBlock .= "
Uncached Template(s): $sizeof ( " . implode('   ', $optlist) . " )
\n"; } $debugBlock .= (sizeof($this->uncached) < 1 ? "
\n" : '') . "
\n"; if (BSRegister::GetType('Db')) { $queries = BSRegister::GetType('Db')->getHistory(); $debugBlock .= "
\n" . '' . "\n\t" . ''; foreach ($queries AS $query) { $debugBlock .= "\n\t"; $debugBlock .= "\n\t\t\n\t"; } $debugBlock .= "\n
Query Debug
"; $debugBlock .= "\n\t\t\t$query[query]\n\n\t\t\t
($query[time])
\n\n\t\t
\n\n\n"; } } $template = str_replace('', $debugBlock . '', $template); print($template); } // ################################################################### /** * Loads an additional template from the database * * @param string The name of the template * * @return string Template data from the database */ protected function _loadTemplate($name) { $path = BSRegister::GetAppPath() . $this->templateDir . $name . '.' . $this->extension; if (is_file($path)) { if (($template = @file_get_contents($path)) !== false) { return $template; } else { trigger_error("Could not load the template '$path'"); exit; } } else { trigger_error("Could not load the template '$path'"); exit; } } // ################################################################### /** * A wrapper for all the parsing functions and compiling functins * * @param string Unparsed template data * * @return string Parsed template data */ protected function _parseTemplate($template) { $template = str_replace('"', '\"', $template); if (function_exists($this->preParseHook)) { $template = call_user_func($this->preParseHook, $template); } $template = $this->_parseBlocksAndTokens($template); $template = $this->_parsePhrases($template); $template = $this->_parseConditionals($template); return $template; } // ################################################################### /** * Parses anything with curly braces {} (including phrases) * * @param string Template data * * @return string Parsed template data */ private function _parseBlocksAndTokens($template) { $stack = array(); $tokens = array(); while (1) { for ($i = 0; $i < strlen($template); $i++) { // we've run through the template and there's nothing in the stack--done if ($i == strlen($template) - 1 AND sizeof($stack) == 0) { return $template; } if ($template[$i] == '{') { // ignore escaped sequences if ($template[$i - 1] != '\\') { array_push($stack, $i); } } else if ($template[$i] == '}') { // there's no stack so it was probably escaped if (sizeof($stack) == 0) { continue; } // we're good and nested else if (sizeof($stack) == 1) { $open = array_pop($stack); $token = substr($template, $open, $i - $open + 1); $template = str_replace($token, $this->_parseToken($token), $template); break; } // just pop it off else { array_pop($stack); } } } } } // ################################################################### /** * Parses a curly brace token {} * * @param string Token * * @return string Parsed value */ private function _parseToken($token) { // knock of the braces $token = substr($token, 1, strlen($token) - 2); // language token if ($token[0] == '@' AND $token[1] == '\\' AND $token[2] == '"') { return '" . ' . $this->langcall . '(\'' . str_replace(array('\\\"', "'"), array('"', "\'"), substr($token, 3, strlen($token) - 5)) . '\')' . ' . "'; } // normal PHP code else { return '" . (' . $token . ') . "'; } } // ################################################################### /** * Prepares language and locale information inside templates * * @param string Template data to be processed * * @return string Language-ready template data */ private function _parsePhrases($template) { $tagStart = ' 0) { // set aside the index and restart capturing $varNum = $capture; $capture = ''; $capturePos = $i; } else { trigger_error('Invalid language variable index "' . $capture . '"'); } } else if ($template[$i] == '>' AND $template[$i - 1] == '"') { // the final variable substitution $varMap[intval($varNum)] = BSFunctions::Substring($template, $capturePos + 3, $i - 2); $varEnds = $i; break; } $capture .= $template[$i]; $i++; } // locate the end tag $end = strpos($template, $tagEnd, $i); if ($end === false) { break; } // this is the string that gets variable replacement $str = BSFunctions::Substring($template, $varEnds + 1, $end); // create the complete varmap for ($i = max(array_keys($varMap)); $i > 0; $i--) { if (!isset($varMap[$i])) { $varMap[$i] = '[MISSING SUBSTITUTION INDEX: ' . $i . ']'; } } // put all the keys in corresponding argument order ksort($varMap); // FINALLY, construct the call to sprintf() $template = substr_replace($template, '" . ' . $this->langconst . '(\'' . $str . '\', "' . implode('", "', $varMap) . '") . "', $start, ($end + strlen($tagEnd)) - $start); } return $template; } // ################################################################### /** * Parser for in-line template conditionals * * @param string Template data awaiting processing * * @return string Parsed template data */ private function _parseConditionals($template) { // tag data $tag_start = ' $relpos = $tag_full['else'] - $tag_full['posi']; // calculate the length of the expression and opening tag $length = strlen($parsed[0]) + strlen($tag_start) + strlen($tag_start_end); // relative to the start of iftrue $elsepos = $relpos - $length; $parsed[1] = substr($conditional, 0, $elsepos); $parsed[2] = substr($conditional, $elsepos + strlen($tag_else)); } // no else to handle else { $parsed[1] = $conditional; $parsed[2] = ''; } // final parsed output $parsed = '" . ((' . stripslashes($parsed[0]) . ') ? "' . $parsed[1] . '" : "' . $parsed[2] . '") . "'; // replace the conditional $template = str_replace($fullspread, $parsed, $template); // reset the parser $offset = $tag_full['posi'] + strlen($tag_start) + strlen($tag_start_end); $tag_full = array(); $stack = array(); $parsed = array(); unset($fullspread, $conditional, $temp_end, $relpos, $length, $elsepos); break; } } } return $template; } } // ################################################################### /** * Debugging function used to print characters in a string that are * around a certain position. * * @access private * * @param string The haystack string * @param integer Position to print around */ function print_around($str, $pos) { echo '>>> PA >>>>>>>>['; echo htmlspecialchars($str[ $pos - 5 ]); echo htmlspecialchars($str[ $pos - 4 ]); echo htmlspecialchars($str[ $pos - 3 ]); echo htmlspecialchars($str[ $pos - 2 ]); echo htmlspecialchars($str[ $pos - 1 ]); echo '©'; echo htmlspecialchars($str[ $pos + 0 ]); echo htmlspecialchars($str[ $pos + 1 ]); echo htmlspecialchars($str[ $pos + 2 ]); echo htmlspecialchars($str[ $pos + 3 ]); echo htmlspecialchars($str[ $pos + 4 ]); echo htmlspecialchars($str[ $pos + 5 ]); echo ']<<<<<<<< PA <<<'; } /*=====================================================================*\ || ################################################################### || # $HeadURL$ || # $Id$ || ################################################################### \*=====================================================================*/ ?>