<?php

/**
 *--------------------------------------
 * PFA默认模板引擎
 *--------------------------------------
 * @project		: pfa
 * @author		: cblee
 * @created		: 2012-9-24
 * @copyright	: (c)2012 AsThis
 *--------------------------------------
 */

class TePfa extends Te
{
	public $tplVars = array(); /* 模板变量 */
	protected $tplFile = ''; /* 当前模板文件名 */
	protected $cacheFile = ''; /* 当前缓存文件 */
	protected $markL = '{-'; /* 左边界符号 */
	protected $markR = '-}'; /* 右边界符号 */
	protected $tagNamespace = ''; /* 标签名称空间 pfa直接使用 */
	protected $tagMarkL = '<'; /* 标签左边界符号 */
	protected $tagMarkR = '>'; /* 标签右边界符号 */
	protected $tplPath = ''; /* 模板文件目录路径 */
	protected $tplTheme = 'default'; /* 模板主题 */
	protected $tplSuffix = '.php'; /* 模板文件后缀名 */
	protected $tplProtection = true; /* 模板文件禁止下载 */
	protected $tplProtectionMark = 'THIS.TEMPLATE.FILE.IS.PROTECTED'; /* 模板保护标记 */
	protected $stripSpace = true; /* 是否去除模板中空格 */
	protected $cacheExpire = 0; /* 缓存时间 0不自动更新 */
	protected $cachePath = ''; /* 缓存文件路径 */
	protected $cacheSuffix = '.php'; /* 缓存文件后缀 */

	public function __construct($options = '')
	{
		$this->options = array(
			'markL' => C('TE.MARK_L'),
			'markR' => C('TE.MARK_R'),
			'tagNamespace' => C('TE.TAG_NAMESPACE'),
			'tagMarkL' => C('TE.TAG_MARK_L'),
			'tagMarkR' => C('TE.TAG_MARK_R'),
			'tplPath' => C('TE.TPL_PATH'),
			'tplTheme' => C('TE.TPL_THEME'),
			'tplSuffix' => C('TE.TPL_SUFFIX'),
			'tplProtection' => C('TE.TPL_PROTECTION'),
			'tplProtectionMark' => C('TE.TPL_PROTECTION_MARK'),
			'stripSpace' => C('TE.STRIP_SPACE'),
			'cacheExpire' => C('TE.CACHE_EXPIRE'),
			'cachePath' => C('TE.CACHE_PATH'),
			'cacheSuffix' => C('TE.CACHE_SUFFIX'),
		);
		if(!empty($options))
		{
			$this->options = array_merge($this->options, $options);
		}
		if(is_array($this->options))
		{
			foreach($this->options as $key => $value)
			{
				$this->$key = $value;
			}
		}
	}

	/* 赋值模板变量 */
	public function assign($name, $value = '')
	{
		if(is_array($name))
		{
			$this->tplVars = array_merge($this->tplVars, $name);
		}
		elseif(is_object($name))
		{
			foreach($name as $key => $val)
			{
				$this->tplVars[$key] = $val;
			}
		}
		else
		{
			$this->tplVars[$name] = $value;
		}
	}

	/* 显示模板 */
	public function display($file, $data = array())
	{
		$this->fetch($file, $data, true);
	}

	/* 创建静态页面 */
	public function build_html($htmlfile, $htmlpath = '', $templateFile = '')
	{
		$content = $this->fetch($templateFile);
		$htmlpath = !empty($htmlpath) ? $htmlpath : APP_PATH.C('HTML.DIR');
		$htmlfile = $htmlpath.D_S.ltrim($htmlfile, '/\\');
		if(!is_dir(dirname($htmlfile)))
		{
			mk_dir(dirname($htmlfile));
		}

		if(false === file_put_contents($htmlfile, $content))
		{
			exit(L('_HTML_BUILD_FAILED_'));
		}
		return $content;
	}

	/* 获取模板内容 */
	public function fetch($file, $data = array(), $display = false)
	{
		$this->tplVars = array_merge($this->tplVars, $data);
		$this->get_fileName($file);
		if(!$this->check_cache())
		{
			$this->build_cache();
		}

		/* 模板变量数组导为变量 */
		extract($this->tplVars, EXTR_OVERWRITE);
		ob_start();
		ob_implicit_flush(0);

		/* 载入模版缓存文件 */
		include($this->cacheFile);
		$content = ob_get_clean();
		if(!$display)
		{
			return $content;
		}
		else
		{
			echo $content;
		}
	}

	/* 获取模板及缓存文件完整路径 */
	private function get_fileName($tplfile = '')
	{
		if('' == $tplfile)
		{
			$tplfile = C('TE.CURRENT_FILE'); /* 默认显示 THEME_PATH[/GROUP_NAME]/CTRLR_NAME/ACTN_NAME.php */
		}
		$this->tplPath = rtrim($this->tplPath, "/\\").D_S;
		$this->cachePath = rtrim($this->cachePath, "/\\").D_S;
		if(!is_file($tplfile))
		{
			$file = str_replace($this->tplSuffix, '', $tplfile).$this->tplSuffix;
			$tplfile = $this->tplPath.$this->tplTheme.D_S.$file;
			if(!is_file($tplfile))
			{
				halt(L('_TPL_FILE_NOT_EXIST_').':'.$tplfile);
			}
		}
		$this->tplFile = $tplfile;
		$this->cacheFile = $this->cachePath.'~'.md5($tplfile).$this->cacheSuffix;
	}

	/* 验证缓存是否有效 */
	public function check_cache()
	{
		if(!file_exists($this->cacheFile))
		{
			return false;
		}
		if(filemtime($this->cacheFile) < filemtime($this->tplFile))
		{
			return false;
		}
		if(filemtime($this->cacheFile) + $this->cacheExpire < time() && 0 != $this->cacheExpire)
		{
			return false;
		}
		return true;
	}

	/* 模板文件 */
	public function build_cache()
	{
		$content = $this->get_tplContent($this->tplFile);
		/* 处理include */
		preg_match_all($this->markL.'include:(.*)'.$this->markR, $content, $include);
		$include = $include[1];
		if(!empty($include))
		{
			/* 当前模板文件所在目录 */
			$currentDir = dirname($this->tplFile);
			foreach($include as $include)
			{
				$file = str_replace($this->tplSuffix, '', $currentDir.D_S.trim($include)).$this->tplSuffix;
				if(file_exists($file))
				{
					$includeContent = $this->get_tplContent($file);
					$content = str_replace($this->markL.'include:'.$include.$this->markR, $includeContent, $content);
				}
			}
		}

		/* 处理pfa标签 */
		$patt = $this->tagMarkL.'pfa:(.+?)'.$this->tagMarkR;
		preg_match_all("/{$patt}/eis", $content, $tags);
		if(is_array($tags[1]))
		{
			$pattEnd = $this->tagMarkL.'\/'.'pfa:(\S+)'.$this->tagMarkR;
			preg_match_all("/{$pattEnd}/eis", $content, $tagsEnd);
			if(count($tags[1]) != count($tagsEnd[1]))
			{
				halt(L('_TPL_TAG_ERROR_'));
			}
			foreach($tags[1] as $t)
			{
				$content = str_replace($this->tagMarkL.'pfa:'.$t.$this->tagMarkR, $this->parse_tag($t), $content);
			}
			foreach($tagsEnd[1] as $t)
			{
				$content = str_replace($this->tagMarkL.'/pfa:'.$t.$this->tagMarkR, "<?php endforeach; endif; ?>", $content);
			}
		}


		/* 处理自定义标签 */
		if($this->tagNamespace)
		{
			foreach(explode(',', $this->tagNamespace) as $tns)
			{
				$tagClass = 'Tag'.ucwords(strtolower($tns));
				import('tag.'.$tagClass, LIB_COMM_PATH);
				$tag = get_instance($tagClass);
				$patt = $this->tagMarkL.$tns.':(.+?)'.$this->tagMarkR;
				preg_match_all("/{$patt}/eis", $content, $tags);
				if(is_array($tags[1]))
				{
					foreach($tags[1] as $t)
					{
						$content = str_replace($this->tagMarkL.$tns.':'.$t.$this->tagMarkR, $tag->parse_tag($t), $content);
					} /* endforeach */
				} /* endif */
				$pattEnd = $this->tagMarkL.'\/'.$tns.':(\S+)'.$this->tagMarkR;
				preg_match_all("/{$pattEnd}/eis", $content, $tagsEnd);
				if(is_array($tags[1]))
				{
					foreach($tagsEnd[1] as $t)
					{
						$content = str_replace($this->tagMarkL.'/'.$tns.':'.$t.$this->tagMarkR, $tag->parse_tag_end($t), $content);
					} /* endforeach */
				} /* endif */
			} /* endforeach */
		}

		$patt = $this->markL.'(\S.+?)'.$this->markR;
		$content = preg_replace("/{$patt}/eis", "\$this->parse_label('\\1')", $content);
		if(ini_get('short_open_tag'))
		{
			/* 开启短标签的情况要将<?标签用echo方式输出 否则无法正常输出xml标识 */
			$content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>'."\r\n", $content);
		}
		$str = "<?php /* PFA 模板缓存文件 生成时间:".date('Y-m-d H:i:s', time())." */ ?>\r\n";
		/* 去除html空格与换行 */
		if($this->stripSpace)
		{
			$find = array("~>\s+<~","~>(\s+\n|\r)~");
			$replace = array("><",">");
			$content = preg_replace($find, $replace, $content);
		}
		$content = $str.$content;
		return file_put_contents($this->cacheFile, $content);
	}

	/* 获取模板文件内容 */
	private function get_tplContent($tplFile)
	{
		$content = file_get_contents($tplFile);
		$_markPre = '<?php /* ';
		$_markSuf = ' */ ?>';
		if($this->tplProtection && strtolower($this->tplSuffix) == '.php')
		{
			/* 已经有保护标记 */
			if($this->tplProtectionMark == substr($content, strlen($_markPre), strlen($this->tplProtectionMark)))
			{
				$_t_markLenth = strlen($_markPre) + strlen($this->tplProtectionMark);
				$content = substr($content, $_t_markLenth, -strlen($_markSuf));
			}
			else
			{
				$contentNew = $_markPre.$this->tplProtectionMark.$content.$_markSuf;
				file_put_contents($tplFile, $contentNew);
			}
		}
		else
		{
			/* 去除保护标记 */
			if($this->tplProtectionMark == substr($content, strlen($_markPre), strlen($this->tplProtectionMark)))
			{
				$_t_markLenth = strlen($_markPre) + strlen($this->tplProtectionMark);
				$content = substr($content, $_t_markLenth, -strlen($_markSuf));
				file_put_contents($tplFile, $content);
			}

		}
		return $content;
	}

	/* 解析pfa标签 */
	private function parse_tag($params)
	{
		$params = explode(' ', $params, 2);
		$params[1] = $params[1].' ';
		preg_match_all('/.*?(\s*.*?=.*?[\"|\'].*?[\"|\']\s).*?/si', $params[1], $arr);
		$a = array();
		if(isset($arr[1]) && !empty($arr[1]))
		{
			foreach($arr[1] as $v)
			{
				$t = explode('=', trim(str_replace(array('"',"'"), '', $v)));
				$a = array_merge($a, array(trim($t[0]) => trim($t[1])));
			}
		}

		$params[0] = parse_name($params[0], 1);
		/* 数据表 */
		$str = "\$_m = M('{$params[0]}');\r\n";

		/* 解析FIELD */
		if(isset($a['field']) && !empty($a['field']))
		{
			$str .= "\$_m->field('{$a['field']}');\r\n";
		}

		/* 解析WHERE */
		if(isset($a['where']) && !empty($a['where']))
		{
			$_t_from = array('eq', 'neq', 'gt', 'egt', 'lt', 'elt', 'notlike','like');
			$_t_to = array('=', '<>', '>', '>=', '<', '<=', 'NOT LIKE', 'LIKE' );
			$where = str_replace($_t_from, $_t_to, $a['where']);
			$str .= "\$_m->where('{$where}');\r\n";
		}

		/* 解析JOIN */
		if(isset($a['join']) && !empty($a['join']))
		{
			$a['join'] = str_replace('eq', '=', $a['join']);
			$str .= "\$_m->join('{$a['join']}');\r\n";
		}

		/* 解析ORDER */
		if(isset($a['order']) && !empty($a['order']))
		{
			$str .= "\$_m->order('{$a['order']}');\r\n";
		}
		/* 解析LIMIT */
		if(isset($a['limit']) && !empty($a['limit']))
		{
			$str .= "\$_m->limit('{$a['limit']}');\r\n";
		}

		/* 解析变量 */
		if(isset($a['as']) && !empty($a['as']))
		{
			$as = $a['as'];
		}
		else
		{
			$as = '$v';
		}

		$str = "<?php\r\n".$str;
		$str .= "\$array = \$_m->select();\r\n";
		$str .= "if(\$array) : foreach(\$array as {$as}): \r\n ?>";
		return $str;
	}

	/* 解析界定符间的内容 根据第一个字符决定使用什么函数进行解析 */
	private function parse_label($label)
	{
		$label = stripslashes(trim($label));
		$flags = array(
			'var' => ':$',
			'language' => ':@',
			'config' => ':#',
			'cookie' => ':+',
			'session' => ':-',
			'get' => ':%',
			'post' => ':&',
			'constant' => ':*',
			'end' => ':/');
		$flag = substr($label, 0, 2);
		if(in_array($flag, $flags))
		{
			$name = substr($label, 2);
			switch($flag)
			{
				/* 普通变量 */
				case $flags['var']:
					return !empty($name) ? $this->parse_var($name) : null;
				/* 输出语言 */
				case $flags['language']:
					return "<?php echo(L(\"{$name}\")); ?>";
				/* 输出配置信息 */
				case $flags['config']:
					return "<?php echo(C(\"{$name}\")); ?>";
				/* 输出Cookie */
				case $flags['cookie']:
					return "<?php echo(ACookie::get(\"{$name}\")); ?>";
				/* 输出SESSION */
				case $flags['session']:
					return "<?php echo(ASession::get(\"{$name}\")); ?>";
				/* 输出GET */
				case $flags['get']:
					return "<?php echo(ARequest::get(\"{$name}\", 'get')); ?>";
				/* 输出POST */
				case $flags['post']:
					return "<?php echo(ARequest::get(\"{$name}\", 'post')); ?>";
				/* 输出常量 */
				case $flags['constant']:
					if('THEME_PATH' == $name)
					{
						return TPL_PATH.($this->tplTheme ? D_S.$this->tplTheme : '');
					}
					if('__THEME__' == $name)
					{
						return __APP__.'/'.TPL_DIR.'/'.($this->tplTheme ? $this->tplTheme : '');
					}
					return "<?php echo({$name}); ?>";
				/* 语句结束部分 */
				case $flags['end']:
					$name = strtolower($name);
					if('foreach' == $name)
					{
						return "<?php endforeach; endif; ?>";
					}
					return "<?php end{$name}; ?>";
			}
		}
		else
		{
			$tags = explode(':', $label, 2);
			$tag = strtolower(trim($tags[0]));
			$params = trim($tags[1]);
			switch($tag)
			{
				case 'import':
					return $this->parse_import($params);
				case 'foreach':
					$arr = trim_array(explode(',', $params));
					if(count($arr) == 3)
					{
						return "<?php if(isset({$arr[0]}) and is_array({$arr[0]})) : foreach({$arr[0]} as {$arr[1]} => {$arr[2]}) : ?>";
					}
					elseif(count($arr) == 2)
					{
						return "<?php if(isset({$arr[0]}) and is_array({$arr[0]})) : foreach({$arr[0]} as {$arr[1]}) : ?>";
					}
					return "<?php if(is_array({$arr[0]})) : foreach({$arr[0]} as \$v) : ?>";
				case 'for':
					$arr = trim_array(explode(',', $params));
					return "<?php for({$arr[0]};{$arr[1]};{$arr[2]}) : ?>";
				case 'if':
					return "<?php if({$params}) :  ?>";
				case 'elseif':
					return "<?php elseif({$params}) :  ?>";
				case 'else':
					return "<?php else : ?>";
				case 'url':
					return "<?php echo(Url::U(\"{$params}\")); ?>";
				case 'php':
					return "<?php {$params} ?>";
			}
		}
		return trim($this->markL, '\\').$label.trim($this->markR, '\\');
	}

	/* 解析变量 $varStr 变量字符串 */
	private function parse_var($varStr)
	{
		static $tVars = array();
		/* 变量|函数参数 */
		$varArray = explode('|', $varStr);
		/* 弹出第一个元素(变量名) */
		$var = array_shift($varArray);
		/* $var.xxx 方式访问数组或属性 */
		if(strpos($var, '.'))
		{
			$vars = explode('.', $var);
			$name = 'is_array($'.$vars[0].') ? $'.$vars[0].'["'.$vars[1].'"] : $'.$vars[0].'->'.$vars[1];
			$var = $vars[0];
		}
		/* $var['xxx'] 方式访问数组 */
		elseif(strpos($var, '['))
		{
			$name = "$".$var;
			preg_match('/(.+?)\[(.+?)\]/is', $var, $arr);
			$var = $arr[1];
		}
		else
		{
			$name = "$$var";
		}
		/* 如果有使用函数 */
		if(count($varArray) > 0)
		{
			/* 传入变量名, 和函数参数继续解析, 这里的变量名是上面的判断设置的值 */
			$name = $this->parse_function($name, $varArray);
		}
		$code = !empty($name) ? "<?php echo({$name}); ?>" : '';
		$tVars[$varStr] = $code;
		return $code;
	}

	/* 解析函数 $varStr:变量名称, $varArray:函数名称及参数 */
	private function parse_function($name, $varArray)
	{
		$len = count($varArray);
		/* 获取不允许使用的函数 */
		$not = explode(',', C('TE.TPL_DENY_FUNC'));
		for($i = 0; $i < $len; $i++)
		{
			/* 以=分割函数参数, 第一个元素就是函数名, 之后的都是参数 */
			$arr = explode('~', $varArray[$i]);
			$funcName = array_shift($arr);
			$arr = array_shift($arr);
			/* 函数名如果不在不允许使用的函数中 */
			if(!in_array($funcName, $not))
			{
				$args = explode(',', $arr);
				if(count($arr) > 0)
				{
					$p = array();
					foreach($args as $var)
					{
						$var = trim($var);
						if($var == '@me')
						{
							$var = $name;
						}
						$p[] = $var;
					}
					$param = join(", ", $p);
					$code = "{$funcName}($param)";
				}
				else
				{
					$code = "{$funcName}($arr[0])";
				}
			}
		}
		return $code;
	}

	/* 解析import标签 */
	private function parse_import($label)
	{
		$param = array(); /* 参数数组 */
		$arr = explode(' ', $label);
		foreach($arr as $v)
		{
			if(strpos($v, '=') > 0)
			{
				$args = explode('=', $v);
				$param[$args[0]] = trim($args[1], '"');
			}
		}

		$fileType = !empty($param['type']) ? strtolower($param['type']) : 'js';

		$param['file'] = str_replace(array('.', '#'), array('/', '.'), $param['file']);
		$files = explode(',', $param['file']);

		$fileName = '~'.str_replace(array(',', '/'), array('-', '.'), $param['file']).'.'.$fileType;

		$basePath = preg_replace_callback('/\((.*)\)/', array('self', 'parse_teConstant'), $param['basepath']);
		$basePath = rtrim($basePath, '/\\').D_S;

		$baseUrl = preg_replace_callback('/\((.*)\)/', array('self', 'parse_teConstant'), $param['baseurl']);
		$baseUrl = rtrim($baseUrl, '/\\').'/';

		$fileUrl = $baseUrl.$fileName;

		/* 创建临时文件 */
		$fileContent = '';
		foreach($files as $file)
		{
			if(file_exists($basePath.$file.'.'.$fileType))
			{
				$fileContent .= file_get_contents($basePath.$file.'.'.$fileType);
			}
			else
			{
				$fileContent .= "\r\n/* load error:".$baseUrl.$file.'.'.$fileType." */\r\n";
			}
		}
		if(!is_dir(dirname($basePath.$fileName)))
		{
			mk_dir(dirname($basePath.$fileName));
		}
		file_put_contents($basePath.$fileName, $fileContent);

		switch($fileType)
		{
			case 'js':
				$str = '<script type="text/javascript" src="'.$fileUrl.'"></script>';
				break;
			case 'css':
				$str = '<link rel="stylesheet" type="text/css" href="'.$fileUrl.'" />';
				break;
		}

		return "<?php echo('{$str}') ?>";
	}

	/* 解析import标签中的常量 */
	private function parse_teConstant($matches)
	{
		if('THEME_PATH' == $matches[1])
		{
			return TPL_PATH.($this->tplTheme ? D_S.$this->tplTheme : '');
		}
		if('__THEME__' == $matches[1])
		{
			return __APP__.'/'.TPL_DIR.'/'.($this->tplTheme ? $this->tplTheme : '');
		}
		/* $matches[1] 是第一个括号中的子模式的匹配项 */
		if(defined($matches[1]))
		{
			return constant($matches[1]);
		}
		return null;
	}
}

?>