autoCompile = true; $this->component = false; } public function render() { if (empty($this->compiledDir)) { $this->compiledDir = dirname($this->templateDir) . "/viewTpl/"; } if (!empty($this->layout)) { include $this->template(true); } else if ($this->component) { return; // 模板内使用{component module action}合并文件 } else { include $this->template(); } } /** * 返回编译后的模板路径, 如果不存在则编译生成并返回路径. * 如果文件存在且允许自动编译, 则对比模板文件和编译后的文件修改时间 * 当修改模板后自支重新编译 * * @param bool $islayout 是否使用布局 * @return string 返回编译后的模板路径 */ public function template($islayout = false) { $this->layoutDir = rtrim($this->layoutDir, '\\/') . '/'; $this->compiledDir = rtrim($this->compiledDir, '\\/') . '/'; $this->templateDir = rtrim($this->templateDir, '\\/') . '/'; if ($islayout) { $tplfile = $this->layoutDir . $this->layout . '.php'; $objfile = $this->compiledDir . 'layout/' . $this->layout . '@' . $this->template . '.php'; } else { $tplfile = $this->templateDir . $this->template . '.php'; $objfile = $this->compiledDir . $this->template . '.php'; } if (is_file($objfile)) { if ($this->autoCompile) { $iscompile = true; $tpl_include_files = include($objfile); $last_modified_time = array(); foreach($tpl_include_files as $f) { $last_modified_time[] = filemtime($f); } if (filemtime($objfile) == max($last_modified_time)) { $iscompile = false; } } else { $iscompile = false; } } else { // 目标文件不存在,编译模板 $iscompile = true; } if ($iscompile) { $this->tpl_include_files[] = $objfile; $this->tpl_include_files[] = $tplfile; $dir = pathinfo($objfile, PATHINFO_DIRNAME); if (!is_dir($dir)) { if (!mkdir($dir, 0777, true)) { trigger_error("Can not create $dir"); } } $str = file_get_contents($tplfile); if (!$str) { trigger_error('Template file Not found or have no access!', E_USER_ERROR); } $str = $this->parse($str); if ($this->autoCompile) { $prefix = "tpl_include_files), true) . ";?>"; $prefix = preg_replace("/([\r\n])+/", "\r\n", $prefix); $postfix = "\r\n\r\n"; } else { $prefix = ''; $postfix = ''; } $str = $prefix . $str . $postfix; if (!file_put_contents($objfile, $str)) { if (file_put_contents($objfile . '.tmp', $str)) { copy($objfile . '.tmp', $objfile); // win下不能重命名已经存在的文件 unlink($objfile . '.tmp'); } } @chmod($objfile,0777); } return $objfile; } /** * 解析{}内字符串,替换php代码 * * @param string $str * @return string */ protected function parse($str) { $str = $this->removeComments($str); $str = $this->parseIncludeComponent($str); // 回车 换行 $str = str_replace("{CR}", "", $str); $str = str_replace("{LF}", "", $str); // if else elseif $str = preg_replace("/\{if\s+(.+?)\}/", "", $str); $str = preg_replace("/\{else\}/", "", $str); $str = preg_replace("/\{elseif\s+(.+?)\}/", "", $str); $str = preg_replace("/\{\/if\}/", "", $str); // loop $str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\}/e", "\$this->addquote('')", $str); $str = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/e", "\$this->addquote('\\3) { ?>')", $str); $str = preg_replace("/\{\/loop\}/", "", $str); // url生成 $str = preg_replace("/\{url\(([^}]+)\)\}/", "generate(\\1);?>", $str); // 函数 $str = preg_replace("/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\s*\(([^{}]*)\))\}/", "", $str); $str = preg_replace("/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "", $str); // 变量 /** * 放弃支持$name.name.name * $str = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\.([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "", $str); */ // 其它变量 $str = preg_replace("/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "", $str); $str = preg_replace("/\{(\\$[a-zA-Z0-9_\.\[\]\'\"\$\x7f-\xff]+)\}/e", "\$this->addquote('')", $str); // 类->属性 类->方法 $str = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff][+\-\>\$\'\"\,\[\]\(\)a-zA-Z0-9_\x7f-\xff]+)\}/es", "\$this->addquote('')", $str); // 常量 $str = preg_replace("/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/", "", $str); // 静态变量 $str = preg_replace("/\{([a-zA-Z0-9_]*::?\\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "", $str); $str = preg_replace("/\{([a-zA-Z0-9_]*::?\\\$[a-zA-Z0-9_\.\[\]\'\"\$\x7f-\xff]+)\}/e", "\$this->addquote('')", $str); // 合并相邻php标记 $str = preg_replace("/\?\>\s*\<\?php[\r\n\t ]*/", "", $str); /** * 删除空行 * Dos和windows采用回车+换行CR/LF表示下一行, * 而UNIX/Linux采用换行符LF表示下一行, * 苹果机(MAC OS系统)则采用回车符CR表示下一行. * CR用符号 '\r'表示, 十进制ASCII代码是13, 十六进制代码为0x0D; * LF使用'\n'符号表示, ASCII代码是10, 十六制为0x0A. * 所以Windows平台上换行在文本文件中是使用 0d 0a 两个字节表示, * 而UNIX和苹果平台上换行则是使用0a或0d一个字节表示. * * 这里统一替换成windows平台回车换行, 第二参数考虑 \\1 保持原有 */ $str = preg_replace("/([\r\n])+/", "\r\n", $str); // 删除第一行 $str = preg_replace("/^[\r\n]+/", "", $str); // write $str = trim($str); return $str; } /** * 变量加上单引号 * 如果是数字就不加单引号, 如果已经加上单引号或者双引号保持不变 */ protected function addquote($var) { preg_match_all("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", $var, $vars); foreach($vars[1] as $k => $v) { if (is_numeric($v)) { $var = str_replace($vars[0][$k], "[$v]", $var); } else { $var = str_replace($vars[0][$k], "['$v']", $var); } } return str_replace("\\\"", "\"", $var); } /** * 模板中第一行可以写exit函数防止浏览 * 删除行首尾空白, html javascript css注释 */ protected function removeComments($str, $clear = false) { $str = str_replace(array('', ''), array('', ''), $str); // 删除行首尾空白 $str = preg_replace("/([\r\n]+)[\t ]+/s", "\\1", $str); $str = preg_replace("/[\t ]+([\r\n]+)/s", "\\1", $str); // 删除 {} 前后的 html 注释 $str = preg_replace("/\<\!\-\-\s*\{(.+?)\}\s*\-\-\>/s", "{\\1}", $str); $str = preg_replace("/\<\!\-\-\s*\-\-\>/s", "", $str); // 删除 html注释 存在 < { 就不删除 $str = preg_replace("/\<\!\-\-\s*[^\<\{]*\s*\-\-\>/s", "", $str); if ($clear) { $str = $this->clear($str); } return $str; } /** * 清除一部分 style script内的注释 * 多行注释内部存在 / 字符就不会清除 */ protected function clear($str) { preg_match_all("|]*>(.*)|Usi", $str, $tvar); foreach($tvar[0] as $k => $v) { // 删除单行注释 $v = preg_replace("/\/\/\s*[a-zA-Z0-9_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/", "", $v); // 删除多行注释 $v = preg_replace("/\/\*[^\/]*\*\//s", "", $v); $str = str_replace($tvar[0][$k], $v, $str); } preg_match_all("|]*>(.*)|Usi", $str, $tvar); foreach($tvar[0] as $k => $v) { // 删除多行注释 $v = preg_replace("/\/\*[^\/]*\*\//s", "", $v); $str = str_replace($tvar[0][$k], $v, $str); } return $str; } /** * * @todo 注意相互引用的模板嵌套会导致死循环 */ protected function parseIncludeComponent($str) { $count_include_component = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar); $count_include_component += preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar); unset($tvar); while ($count_include_component > 0) { $str = $this->parseInclude($str); $str = $this->parseComponent($str); $count_include_component = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar); $count_include_component += preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar); unset($tvar); } $str = $this->removeComments($str); return $str; } /** * 解析多个{include path/file}合并成一个文件 * * @example {include 'debug_info'} * {include 'debug_info.php'} * {include "debug_info"} * {include "debug_info.php"} * {include $this->templateDir . $this->template} */ private function parseInclude($str) { $countSubTpl = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar); while ($countSubTpl > 0) { foreach($tvar[1] as $k => $subfile) { eval("\$subfile = $subfile;"); if (is_file($subfile)) { $findfile = $subfile; } else if (is_file($subfile . '.php')) { $findfile = $subfile . '.php'; } else if (is_file($this->templateDir . $subfile)) { $findfile = $this->templateDir . $subfile; } else if (is_file($this->templateDir . $subfile . '.php')) { $findfile = $this->templateDir . $subfile . '.php'; } else { $findfile = ''; } if (!empty($findfile)) { $subTpl = file_get_contents($findfile); $this->tpl_include_files[] = $findfile; } else { // 找不到文件 $subTpl = 'SubTemplate not found:' . $subfile; } $str = str_replace($tvar[0][$k], $subTpl, $str); } $countSubTpl = preg_match_all("/\{include\s+(.+)\}/", $str, $tvar); } return $str; } /** * 解析多个{component module action}合并成一个文件 */ private function parseComponent($str) { $countCom = preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar); while ($countCom > 0) { $i = 0; while ($i < $countCom) { $comfile = $this->templateDir . "component/" . $tvar[1][$i] . '-' . $tvar[2][$i] . '.php'; if (is_file($comfile)) { $subTpl = file_get_contents($comfile); $this->tpl_include_files[] = $comfile; } else { $subTpl = 'SubTemplate not found:' . $comfile; } //////////////////////////////////////////////////////////////////////////// $module = $tvar[1][$i]; $action = $tvar[2][$i]; $subTpl = "dispatchComponent('$module', '$action', \$this->context); \$comdata = \$dispatcher->data; unset(\$dispatcher); ?> " . $subTpl; //////////////////////////////////////////////////////////////////////////// $str = str_replace($tvar[0][$i], $subTpl, $str); $i++; } $countCom = preg_match_all("/\{component\s+([a-zA-Z0-9\.\-_]+)\s+([a-zA-Z0-9\.\-_]+)\}/", $str, $tvar); } return $str; } }