...) - pozor na skryté chyby
- neobejde se bez ukládání zkompilovaných souborů do cache, protože se následně includují
- rozšíření - je volána uživatelská funkce format_date|number|price, JS, TMPL_SELECT, TMPL_ELSEIF, TMPL_ELSEUNLESS
//! TMPL_LOOP_INT by se dal nahradit TMPL_LOOP + array_fill(0, $num, array()), pouze $num != 0
co mi chybí: OR v podmínkách
*/
// zpracování vložených souborů - vložené soubory nejsou kompilované, protože se mohou vyskytovat v různých hloubkách rekurze a musí dostat i $vars (kvůli global)
function htmltmpl_compile_include($filename, $recursion = array())
{
if (isset($recursion[$filename])) {
exit("Recursive include of $filename.\n");
}
$recursion[$filename] = true;
$file = file_get_contents($filename);
$file = preg_replace_callback('~" ]+)[^>]*>~i', create_function('$matches', 'return htmltmpl_compile_include("' . dirname($filename) . '/" . $matches[1], ' . var_export($recursion, true) . ');'), $file);
return $file;
}
// překlad TMPL značek, callback funkce preg_replace_callback
function htmltmpl_compile_tag($matches)
{
static $loops = array();
static $loops_used = array();
$tag = strtoupper($matches[2]);
$attrs = preg_replace('~(/| --)+$~', '', $matches[3]);
if ($tag == "TMPL_ELSE") {
$return = "";
} elseif ($tag == "TMPL_BOUNDARY") {
$return = "";
} elseif ($tag == "/TMPL_IF" || $tag == "/TMPL_UNLESS") {
$return = "";
} elseif ($tag == "/TMPL_LOOP" || $tag == "/TMPL_LOOP_INT") {
array_pop($loops);
$return = "";
} else { // značky obsahující atribut NAME
$name = (preg_match('~\\sNAME=("[^"]+"|[^ ]+)~i', $attrs, $matches2) ? trim($matches2[1], '"') : preg_replace('~^\\s+([^\\s]+).*~s', '\\1', $attrs));
/* zpracováno dříve
if ($tag == "TMPL_INCLUDE") {
$return = "";
} else */
if ($tag == "TMPL_STATIC") {
$return = "$matches[1]$matches[4]";
} elseif ($tag == "TMPL_SELECT") {
$return = "";
} elseif ($tag == "TMPL_LOOP") {
$loop = count($loops) + 1;
$var_name = ($loop == 1 ? "vars" : "row" . ($loop - 1));
$loops[] = $name;
$return = " \$row$loop) { ?>";
} elseif ($tag == "TMPL_LOOP_INT") {
$loops[] = ":$name";
$loop = count($loops);
$return = "";
} else {
$loop = count($loops);
$count = (substr(end($loops), 0, 1) == ":" ? '$vars["' . substr(end($loops), 1) . '"]' : 'count($vars["' . end($loops) . '"])');
switch ($name) {
case '__FIRST__': $return = "\$i$loop == 0"; break;
case '__LAST__': $return = "\$i$loop == $count-1"; break;
case '__INNER__': $return = "\$i$loop != 0 && \$i$loop != $count-1"; break;
case '__ODD__': $return = "\$i$loop % 2 == 0"; break;
case '__PASS__': $return = "\$i$loop + 1"; break;
case '__PASSTOTAL__': $return = $count; break;
default: if (preg_match('~^__EVERY__([1-9][0-9]*)$~', $name, $matches2)) {
$return = "\$i$loop % $matches2[1] == 0";
} else {
$global = (!$loop || preg_match('~\\sGLOBAL="?1~i', $attrs));
$return = ($global ? "\$vars[\"$name\"]" : '$row' . $loop . "[\"$name\"]");
/* hledání v nadřazených polích - odpovídá funkčnosti s global_vars
for ($i = ($global ? 1 : 2); $i <= $loop; $i++) {
$return = "(isset(\$row" . $i . "[\"$name\"]) ? \$row" . $i . "[\"$name\"] : $return)";
}
*/
if ($tag == "TMPL_VAR") {
preg_match('~\\sESCAPE="?([^\\s"]+)~i', $attrs, $matches2);
switch (strtoupper($matches2[1])) {
case 'NONE': $return = "(is_array($return) ? count($return) : $return)"; break;
case 'DATE':
case 'TIME':
case 'NUMBER':
case 'PRICE': $return = "format_" . strtolower($matches2[1]) . "($return)"; break;
case 'JS': $return = 'addcslashes(' . $return . ', "\'\r\n\\\\")'; break;
case 'URL': $return = "urlencode($return)"; break;
case 'WAP': $return = "str_replace('\$', '\$\$', $return)"; // break left intentionally
case 'HTML':
default: $return = "nl2br(htmlspecialchars($return))";
}
} elseif (preg_match('~\\sVALUE=("[^"]+"|[^ ]+)~i', $attrs, $matches2)) {
$return .= ' == "' . addcslashes(html_entity_decode(trim($matches2[1], '"')), '\\"') . '"';
} else {
//~ $return = "!empty($return)";
}
}
}
if ($tag == "TMPL_VAR") {
$return = "$matches[1]$matches[4]"; // zachovat mezery na začátku řádku a \n na konci
} elseif ($tag == "TMPL_IF" || $tag == "TMPL_ELSEIF") {
$return = "";
} elseif ($tag == "TMPL_UNLESS" || $tag == "TMPL_ELSEUNLESS") {
$return = "";
} else { // neznámá značka
trigger_error("Unknown tag $tag", E_USER_WARNING);
return $matches[0];
}
}
}
return "$return$matches[4]";
}
// překlad jazykových značek
function htmltmpl_compile_lang($matches)
{
static $lang_function;
if (is_string($matches)) {
$lang_function = $matches;
return;
}
$backslashes = substr($matches[1], 0, floor(strlen($matches[1]) / 2));
if (strlen($matches[1]) % 2 == 1) { // escapované [[
return $backslashes . "[[$matches[2]]]$matches[3]";
}
$return = "$lang_function('" . addcslashes($matches[2], "\\'") . "'" . (trim($matches[3]) ? ", false" : "") . ")";
return $backslashes . ($lang_function ? "" : $matches[2]) . $matches[3] . (!trim($matches[3]) ? $matches[3] : "");
}
// kompilace šablony $filename do PHP kódu
function htmltmpl_compile($filename, $lang_function = "", $cache_dir = "")
{
htmltmpl_compile_lang($lang_function);
$cache_filename = (!$cache_dir ? "./" : $cache_dir) . basename($filename) . "c"; //! basename
if (!file_exists($cache_filename) || filemtime($filename) > filemtime($cache_filename)) {
$file = htmltmpl_compile_include($filename);
$file = preg_replace('~<\\?~', "", $file);
$file = preg_replace_callback("~[ \t]*### (.*)~", create_function('$matches', 'return "";'), $file);
$file = preg_replace_callback("~(\\\\*)\\[\\[(.*?)\\]\\]([\"']|(?: -)?|\r?\n)?~s", 'htmltmpl_compile_lang', $file);
$file = preg_replace_callback("~(^[ \t]+)?<(?:!-- )?(/?TMPL_[^\\s>]*)([^>]*)>(\r?\n?)~im", 'htmltmpl_compile_tag', $file);
$tempnam = tempnam((!$cache_dir ? "./" : $cache_dir), "tmp"); // při zápisu rovnou do $cache_filename by se před zapsáním dat vkládal prázdný soubor
$fp = fopen($tempnam, "wb");
fwrite($fp, $file);
fclose($fp);
copy($tempnam, $cache_filename); // rename() není použito proto, že soubor už mohl existovat a je potřeba, aby ani na chvíli nezmizel, takže ho nelze smazat
unlink($tempnam);
}
return $cache_filename;
}
// zkompilování a vložení šablony s využitím proměnných $vars
function htmltmpl_include($filename, $vars, $lang_function = "", $cache_dir = "")
{
// z funkce to je vloženo proto, že zkompilovaný kód používá proměnnou $vars
include htmltmpl_compile($filename, $lang_function, $cache_dir);
}