<?php
/////////////////////////////////////////////////////////////////
// SpeedPHP中文PHP框架, Copyright (C) 2008 - 2010 SpeedPHP.com //
/////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////
//地铁客开源轻博客, Copyright (C)   2010 - 2011  www.ditieker.com
//EMAIL:ditieker@163.com QQ:530548182
//$Id: spModel.php 47 2011-11-08 04:57:23Z aeomin $

/**
 * spModel 系统模型类，所有模型类的父类 应用程序中的每个模型类都应继承于spModel。
 */
class spModel {
	/**
	 * 供检验值的规则与返回信息
	 */
	public $verifier = null;

	/**
	 * 增加的自定义验证函数
	 */
	public $addrules = array();
	/**
	 * 表主键
	 */
	public $pk;
	/**
	 * 表名称
	 */
	public $table;

	/**
	 * 关联描述
	 */
	public $linker = null;

	/**
	 * 表全名
	 */
	public $tbl_name = null;

	/**
	 * 数据驱动程序
	 */
	public $_db;

	/**
	 * 构造函数
	 */
	public function __construct()
	{
		if( null == $this->tbl_name )$this->tbl_name = $GLOBALS['G_SP']['db']['prefix'] . $this->table;
		if( '' == $GLOBALS['G_SP']['db_driver_path'] ){
			$GLOBALS['G_SP']['db_driver_path'] = $GLOBALS['G_SP']['sp_drivers_path'].'/'.$GLOBALS['G_SP']['db']['driver'].'.php';
		}
		$this->_db = spClass('db_'.$GLOBALS['G_SP']['db']['driver'], array(0=>$GLOBALS['G_SP']['db']), $GLOBALS['G_SP']['db_driver_path']);
	}

	/**
	 * 从数据表中查找一条记录
	 *
	 * @param conditions    查找条件，数组array("字段名"=>"查找值")或字符串，
	 * 请注意在使用字符串时将需要开发者自行使用escape来对输入值进行过滤
	 * @param sort    排序，等同于“ORDER BY ”
	 * @param fields    返回的字段范围，默认为返回全部字段的值
	 */
	public function find($conditions = null, $sort = null, $fields = null)
	{
		if( $record = $this->findAll($conditions, $sort, $fields, 1) ){
			return array_pop($record);
		}else{
			return FALSE;
		}
	}

	/**
	 * 从数据表中查找记录
	 *
	 * @param conditions    查找条件，数组array("字段名"=>"查找值")或字符串，
	 * 请注意在使用字符串时将需要开发者自行使用escape来对输入值进行过滤
	 * @param sort    排序，等同于“ORDER BY ”
	 * @param fields    返回的字段范围，默认为返回全部字段的值
	 * @param limit    返回的结果数量限制，等同于“LIMIT ”，如$limit = " 3, 5"，即是从第3条记录（从0开始计算）开始获取，共获取5条记录
	 *                 如果limit值只有一个数字，则是指代从0条记录开始。
	 */
	public function findAll($conditions = null, $sort = null, $fields = null, $limit = null)
	{
		$where = "";
		$fields = empty($fields) ? "*" : $fields;
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		if(null != $sort){
			$sort = "ORDER BY {$sort}";
		}else if (null != $this->pk){
			$sort = "ORDER BY {$this->pk}";
		}
		$sql = "SELECT {$fields} FROM {$this->tbl_name} {$where} {$sort}";
		if(null != $limit)$sql = $this->_db->setlimit($sql, $limit);
		return $this->_db->getArray($sql);
	}
	/**
	 * 过滤转义字符
	 *
	 * @param value 需要进行过滤的值
	 */
	public function escape($value)
	{
		return $this->_db->__val_escape($value);
	}
	// __val_escape是val的别名，向前兼容
	public function __val_escape($value){return $this->escape($value);}

	/**
	 * 在数据表中新增一行数据
	 *
	 * @param row 数组形式，数组的键是数据表中的字段名，键对应的值是需要新增的数据。
	 */
	public function create($row)
	{
		if(!is_array($row))return FALSE;
		$row = $this->__prepera_format($row);
		if(empty($row))return FALSE;
		foreach($row as $key => $value){
			$cols[] = $key;
			$vals[] = $this->escape($value);
		}
		$col = join(',', $cols);
		$val = join(',', $vals);

		$sql = "INSERT INTO {$this->tbl_name} ({$col}) VALUES ({$val})";
		if( FALSE != $this->_db->exec($sql) ){ // 获取当前新增的ID
			if( $newinserid = $this->_db->newinsertid() ){
				return $newinserid;
			}else{
				return array_pop( $this->find($row, ($this->pk)?"{$this->pk} DESC":'',$this->pk) );
			}
		}
		return FALSE;
	}

	/**
	 * 在数据表中新增多条记录
	 *
	 * @param rows 数组形式，每项均为create的$row的一个数组
	 */
	public function createAll($rows)
	{
		foreach($rows as $row)$this->create($row);
	}

	/**
	 * 按条件删除记录
	 *
	 * @param conditions 数组形式，查找条件，此参数的格式用法与find/findAll的查找条件参数是相同的。
	 */
	public function delete($conditions)
	{
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ( ".join(" AND ",$join). ")";
		}else{
			if(null != $conditions)$where = "WHERE ( ".$conditions. ")";
		}
		$sql = "DELETE FROM {$this->tbl_name} {$where}";
		return $this->_db->exec($sql);
	}

	/**
	 * 按字段值查找一条记录
	 *
	 * @param field 字符串，对应数据表中的字段名
	 * @param value 字符串，对应的值
	 */
	public function findBy($field, $value)
	{
		return $this->find(array($field=>$value));
	}

	/**
	 * 按字段值修改一条记录
	 *
	 * @param conditions 数组形式，查找条件，此参数的格式用法与find/findAll的查找条件参数是相同的。
	 * @param field 字符串，对应数据表中的需要修改的字段名
	 * @param value 字符串，新值
	 */
	public function updateField($conditions, $field, $value)
	{
		return $this->update($conditions, array($field=>$value));
	}

	/**
	 * 使用SQL语句进行查找操作，等于进行find，findAll等操作
	 *
	 * @param sql 字符串，需要进行查找的SQL语句
	 */
	public function findSql($sql)
	{
		return $this->_db->getArray($sql);
	}

	/**
	 * 执行SQL语句，相等于执行新增，修改，删除等操作。
	 *
	 * @param sql 字符串，需要执行的SQL语句
	 */
	public function runSql($sql)
	{
		return $this->_db->exec($sql);
	}
	// query是runSql的别名，向前兼容
	public function query($sql){return $this->runSql($sql);}

	/**
	 * 返回最后执行的SQL语句供分析
	 */
	public function dumpSql()
	{
		return end( $this->_db->arrSql );
	}

	/**
	 * 返回上次执行update,create,delete,exec的影响行数
	 */
	public function affectedRows()
	{
		return $this->_db->affected_rows();
	}
	/**
	 * 计算符合条件的记录数量
	 *
	 * @param conditions 查找条件，数组array("字段名"=>"查找值")或字符串，
	 * 请注意在使用字符串时将需要开发者自行使用escape来对输入值进行过滤
	 */
	public function findCount($conditions = null)
	{
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		$sql = "SELECT COUNT({$this->pk}) AS SP_COUNTER FROM {$this->tbl_name} {$where}";
		$result = $this->_db->getArray($sql);
		return $result[0]['SP_COUNTER'];
	}

	/**
	 * 魔术函数，执行模型扩展类的自动加载及使用
	 */
	public function __call($name, $args)
	{
		if(in_array($name, $GLOBALS['G_SP']["auto_load_model"])){
			return spClass($name)->__input($this, $args);
		}elseif(!method_exists( $this, $name )){
			spError("方法 {$name} 未定义");
		}
	}

	/**
	 * 修改数据，该函数将根据参数中设置的条件而更新表中数据
	 *
	 * @param conditions    数组形式，查找条件，此参数的格式用法与find/findAll的查找条件参数是相同的。
	 * @param row    数组形式，修改的数据，
	 *  此参数的格式用法与create的$row是相同的。在符合条件的记录中，将对$row设置的字段的数据进行修改。
	 */
	public function update($conditions, $row)
	{
		$where = "";
		$row = $this->__prepera_format($row);
		if(empty($row))return FALSE;
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		foreach($row as $key => $value){
			$value = $this->escape($value);
			$vals[] = "{$key} = {$value}";
		}
		$values = join(", ",$vals);
		$sql = "UPDATE {$this->tbl_name} SET {$values} {$where}";
		return $this->_db->exec($sql);
	}

	/**
	 * 替换数据，根据条件替换存在的记录，如记录不存在，则将条件与替换数据相加并新增一条记录。
	 *
	 * @param conditions    数组形式，查找条件，请注意，仅能使用数组作为该条件！
	 * @param row    数组形式，修改的数据
	 */
	public function replace($conditions, $row)
	{
		if( $this->find($conditions) ){
			return $this->update($conditions, $row);
		}else{
			if( !is_array($conditions) )spError('replace方法的条件务必是数组形式！');
			$rows = spConfigReady($conditions, $row);
			return $this->create($rows);
		}
	}

	/**
	 * 为设定的字段值增加
	 * @param conditions    数组形式，查找条件，此参数的格式用法与find/findAll的查找条件参数是相同的。
	 * @param field    字符串，需要增加的字段名称，该字段务必是数值类型
	 * @param optval    增加的值
	 */
	public function incrField($conditions, $field, $optval = 1)
	{
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->escape($condition);
				$join[] = "{$key} = {$condition}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		$values = "{$field} = {$field} + {$optval}";
		$sql = "UPDATE {$this->tbl_name} SET {$values} {$where}";
		return $this->_db->exec($sql);
	}

	/**
	 * 为设定的字段值减少
	 * @param conditions    数组形式，查找条件，此参数的格式用法与find/findAll的查找条件参数是相同的。
	 * @param field    字符串，需要减少的字段名称，该字段务必是数值类型
	 * @param optval    减少的值
	 */
	public function decrField($conditions, $field, $optval = 1)
	{
		return $this->incrField($conditions, $field, - $optval);
	}

	/**
	 * 按给定的数据表的主键删除记录
	 *
	 * @param pk    字符串或数字，数据表主键的值。
	 */
	public function deleteByPk($pk)
	{
		return $this->delete(array($this->pk=>$pk));
	}

	public function getVersion()
	{
		$sql = 'select version() as v';
		$rs = $this->_db->getArray($sql);
		return $rs[0]['v'];
	}


	/**
	 * 按表字段调整适合的字段
	 * @param rows    输入的表字段
	 */
	private function __prepera_format($rows)
	{
		$columns = $this->_db->getTable($this->tbl_name);
		$newcol = array();
		foreach( $columns as $col ){
			$newcol[$col['Field']] = $col['Field'];
		}
		return array_intersect_key($rows,$newcol);
	}

}


/**
 * spPager
 * 数据分页程序
 */
class spPager {
	/**
	 * 模型对象
	 */
	private $model_obj = null;
	/**
	 * 页码数据
	 */
	private $pageData = null;
	/**
	 * 调用时输入的参数
	 */
	private $input_args = null;
	/**
	 * 函数式使用模型辅助类的输入函数
	 */
    public function __input(& $obj, $args){
		$this->model_obj = $obj;
		$this->input_args = $args;
		return $this;
	}
	/**
	 * 魔术函数，支持多重函数式使用类的方法
	 */
	public function __call($func_name, $func_args){
		if( ( 'findAll' == $func_name || 'findSql' == $func_name ) && 0 != $this->input_args[0]){
			return $this->runpager($func_name, $func_args);
		}elseif(method_exists($this,$func_name)){
			return call_user_func_array(array($this, $func_name), $func_args);
		}else{
			return call_user_func_array(array($this->model_obj, $func_name), $func_args);
		}
	}
	/**
	 * 获取分页数据
	 */
	public function getPager(){
		return $this->pageData;
	}

	/**获取html版本的分页**/
	public function pagerHtml($c='main',$a='index',$filter = array())
	{
		 $pager =$this->getPager();
		 $pages = $this->makepage($pager); //获得截断的pages
		 $pageHtml = Null;

		if($pager)
		{
			//显示头部
			if($pager['current_page'] != $pager['first_page'])
			{
				$pageHtml .= '<a href="'.spUrl($c,$a, array_merge(array('page'=>$pager['first_page']),$filter)).'">&laquo; 第一页</a>
						<a href="'.spUrl($c,$a,array_merge(array('page'=>$pager['prev_page']),$filter)).'">&laquo; 上一页</a>';
			}

			//显示中间
			 foreach($pages as $thepage)
			 {
				 	if($thepage != $pager['current_page'])
							{
								$pageHtml .= '<a href="'.spUrl($c,$a,array_merge(array('page'=>$thepage),$filter)).'" class="number">'.$thepage.'</a> ';
							}else{
								$pageHtml .= '<a href="javascript:void(0)" class="number current"><b>'.$thepage.'</b></a>';
							}
			 }

			 //显示尾页
				if($pager['current_page'] != $pager['last_page'])
				{
					$pageHtml .= '<a href="'.spUrl($c,$a,array_merge(array('page'=>$pager['next_page']),$filter)).'">下一页 &raquo;</a>
										<a href="'.spUrl($c,$a,array_merge(array('page'=>$pager['last_page']),$filter)).'">尾页 &raquo;</a>';
				}
					//$pageHtml .=  $pager['total_count'].'/'.$pager['total_page'].'/'.$pager['page_size'];
		 }

		 return $pageHtml;

	}

		/**获取html版本的分页**/
	public function pagerAjax($c='main',$a='index',$filter = array(),$runarea='')
	{
		 $pager =$this->getPager();
		 $pages = $this->makepage($pager); //获得截断的pages
		 $pageHtml = Null;

		if($pager)
		{
			//显示头部
			if($pager['current_page'] != $pager['first_page'])
			{
				$url = spUrl($c,$a, array_merge(array('page'=>$pager['first_page']),$filter));
				$next = spUrl($c,$a,array_merge(array('page'=>$pager['prev_page']),$filter));

				$pageHtml .= '<a href="javascript:void(0)" onclick="runpage(\''.$url.'\',\''.$runarea.'\')">&laquo; 第一页</a>
						<a href="javascript:void(0)" onclick="runpage(\''.$next.'\',\''.$runarea.'\')">&laquo; 上一页</a>';
			}

			//显示中间
			 foreach($pages as $thepage)
			 {
				 	if($thepage != $pager['current_page'])
							{
								$url = spUrl($c,$a,array_merge(array('page'=>$thepage),$filter));
								$pageHtml .= '<a href="javascript:void(0)" onclick="runpage(\''.$url.'\',\''.$runarea.'\')" class="number">'.$thepage.'</a> ';
							}else{
								$pageHtml .= '<a href="javascript:void(0)" class="number current"><b>'.$thepage.'</b></a>';
							}
			 }

			 //显示尾页
				if($pager['current_page'] != $pager['last_page'])
				{
					$next = spUrl($c,$a,array_merge(array('page'=>$pager['next_page']),$filter));
					$end = spUrl($c,$a,array_merge(array('page'=>$pager['last_page']),$filter));

					$pageHtml .= '<a href="javascript:void(0)" onclick="runpage(\''.$next.'\',\''.$runarea.'\')">下一页 &raquo;</a>
										<a href="javascript:void(0)" onclick="runpage(\''.$end.'\',\''.$runarea.'\')">尾页 &raquo;</a>';
				}
					//$pageHtml .=  $pager['total_count'].'/'.$pager['total_page'].'/'.$pager['page_size'];
		 }

		 return $pageHtml;

	}


	/*进行页码截断*/
	private function makepage($pager, $offset = 4) //这里的$offset表示当前页的左右偏移量
	{
		$start = 1;
		$end = $offset * 2+1;
		if ($pager['current_page'] > $offset)
		{
			$start = $pager['current_page'] - $offset;
			$end = $pager['current_page'] + $offset;
		}
		if ($end > $pager['total_page'])
		{
			$start = $pager['total_page'] - $offset * 2;
			$end = $pager['total_page'];
		}

		if ($start < 1) $start = 1;
		return array_slice($pager['all_pages'], $start - 1, $end - $start+1, true);
	}


	/**
	 * 生成分页数据
	 */
	private function runpager($func_name, $func_args){
		$this->pageData = null;
		$page = $this->input_args[0];
		$pageSize = $this->input_args[1];
		@list($conditions, $sort, $fields ) = $func_args;
		if('findSql'==$func_name){
			$total_count = array_pop( array_pop( $this->model_obj->findSql("SELECT COUNT({$this->model_obj->pk}) as sp_counter FROM ($conditions) sp_tmp_table_pager1") ) );
		}else{
			$total_count = $this->model_obj->findCount($conditions);
		}
		if($total_count > $pageSize){
			$total_page = ceil( $total_count / $pageSize );
			$page = min(intval(max($page, 1)), $total_count); // 对页码进行规范运算
			$this->pageData = array(
				"total_count" => $total_count,                                 // 总记录数
				"page_size"   => $pageSize,                                    // 分页大小
				"total_page"  => $total_page,                                  // 总页数
				"first_page"  => 1,                                            // 第一页
				"prev_page"   => ( ( 1 == $page ) ? 1 : ($page - 1) ),         // 上一页
				"next_page"   => ( ( $page == $total_page ) ? $total_page : ($page + 1)),     // 下一页
				"last_page"   => $total_page,                                  // 最后一页
				"current_page"=> $page,                                        // 当前页
				"all_pages"   => array()	                                   // 全部页码
			);
			for($i=1; $i <= $total_page; $i++)$this->pageData['all_pages'][] = $i;
			$limit = ($page - 1) * $pageSize . "," . $pageSize;
			if('findSql'==$func_name)$conditions = $this->model_obj->_db->setlimit($conditions, $limit);
		}
		if('findSql'==$func_name){
			return $this->model_obj->findSql($conditions);
		}else{
			return $this->model_obj->findAll($conditions, $sort, $fields, $limit);
		}
	}
}

/**
 * spVerifier
 * 数据验证程序
 */
class spVerifier {

	/**
	 * 附加的检验规则函数
	 */
	private $add_rules = null;

	/**
	 * 验证规则
	 */
	private $verifier = null;

	/**
	 * 验证时返回的提示信息
	 */
	private $messages = null;

	/**
	 * 待验证字段
	 */
	private $checkvalues = null;
	/**
	 * 函数式使用模型辅助类的输入函数
	 */
    public function __input(& $obj, $args){
		$this->verifier = (null != $obj->verifier) ? $obj->verifier : array();
		if(isset($args[1]) && is_array($args[1])){
			$this->verifier["rules"] = $this->verifier["rules"] + $args[1]["rules"];
			$this->verifier["messages"] = isset($args[1]["messages"]) ? ( $this->verifier["messages"] + $args[1]["messages"] ) : $this->verifier["messages"];
		}
		if(is_array($obj->addrules) && !empty($obj->addrules) ){foreach($obj->addrules as $addrule => $addveri)$this->addrules($addrule, $addveri);}
		if(empty($this->verifier["rules"]))spError("无对应的验证规则！");
		return is_array($args[0]) ? $this->checkrules($args[0]) : TRUE; // TRUE为不通过验证
	}

	/**
	 * 加入附加的验证规则
	 *
	 * @param rule_name    验证规则名称
	 * @param checker    验证器，验证器可以有两种方式：
	 * 第一种是  '验证函数名'，这是当函数是一个单纯的函数时使用
	 * 第二种是 array('类名', '方法函数名')，这是当函数是一个类的某个方法函数时候使用。
	 */
	public function addrules($rule_name, $checker){
		$this->add_rules[$rule_name] = $checker;
	}
	/**
	 * 按规则验证数据
	 *
	 * @param values    验证值
	 */
	private function checkrules($values){
		$this->checkvalues = $values;
		foreach( $this->verifier["rules"] as $rkey => $rval ){
			$inputval = isset($values[$rkey]) ? $values[$rkey] : '';
			foreach( $rval as $rule => $rightval ){
				if(method_exists($this, $rule)){
					if(TRUE == $this->$rule($inputval, $rightval))continue;
				}elseif(null != $this->add_rules && isset($this->add_rules[$rule])){
					if( function_exists($this->add_rules[$rule]) ){
						if(TRUE == $this->add_rules[$rule]($inputval, $rightval, $values))continue;
					}elseif( is_array($this->add_rules[$rule]) ){
						if(TRUE == spClass($this->add_rules[$rule][0])->{$this->add_rules[$rule][1]}($inputval, $rightval, $values))continue;
					}
				}else{
					spError("未知规则！");
				}
				$this->messages[$rkey][] = (isset($this->verifier["messages"][$rkey][$rule])) ? $this->verifier["messages"][$rkey][$rule] : "{$rule}";
			}
		}
		// 返回FALSE则通过验证，返回数组则未能通过验证，返回的是提示信息。
		return (null == $this->messages) ? FALSE : $this->messages;
	}
	/**
	 * 内置验证器，检查字符串非空
	 * @param val    待验证字符串
	 * @param right    正确值
	 */
	private function notnull($val, $right){return $right === ( strlen($val) > 0 );}
	/**
	 * 内置验证器，检查字符串是否小于指定长度
	 * @param val    待验证字符串
	 * @param right    正确值
	 */
	private function minlength($val, $right){return $this->cn_strlen($val) >= $right;}
	/**
	 * 内置验证器，检查字符串是否大于指定长度
	 * @param val    待验证字符串
	 * @param right    正确值
	 */
	private function maxlength($val, $right){return $this->cn_strlen($val) <= $right;}
	/**
	 * 内置验证器，检查字符串是否等于另一个验证字段的值
	 * @param val    待验证字符串
	 * @param right    正确值
	 */
	private function equalto($val, $right){return $val == $this->checkvalues[$right];}
	/**
	 * 内置验证器，检查字符串是否正确的时间格式
	 * @param val    待验证字符串
	 * @param right    正确值
	 */
	private function istime($val, $right){$test = @strtotime($val);return $right == ( $test !== -1 && $test !== false );}
	/**
	 * 内置验证器，检查字符串是否正确的电子邮件格式
	 * @param val    待验证字符串
	 * @param right    正确值
	 */
	private function email($val, $right){
		return $right == ( preg_match('/^[A-Za-z0-9]+([._\-\+]*[A-Za-z0-9]+)*@([A-Za-z0-9-]+\.)+[A-Za-z0-9]+$/', $val) != 0 );
	}
	/**
	 * 计算字符串长度，支持包括汉字在内的字符串
	 * @param val    待计算的字符串
	 */
	public function cn_strlen($val){$i=0;$n=0;
		while($i<strlen($val)){$clen = ( strlen("快速") == 4 ) ? 2 : 3;
			if(preg_match("/^[".chr(0xa1)."-".chr(0xff)."]+$/",$val[$i])){$i+=$clen;}else{$i+=1;}$n+=1;}
		return $n;
	}
}

/**
 * spCache
 * 函数和数据缓存实现
 */
class spCache {

	/**
	 * 默认的数据生存期
	 */
	public $life_time = 3600;

	/**
	 * 模型对象
	 */
	private $model_obj = null;

	/**
	 * 调用时输入的参数
	 */
	private $input_args = null;
	/**
	 * 函数式使用模型辅助类的输入函数
	 */
    public function __input(& $obj, $args){
		$this->model_obj = $obj;
		$this->input_args = $args;
		return $this;
	}
	/**
	 * 魔术函数，支持多重函数式使用类的方法
	 */
	public function __call($func_name, $func_args){
		if( isset($this->input_args[0]) && -1 == $this->input_args[0] )return $this->clear($this->model_obj, $func_name, $func_args);
		$cache_id = get_class($this->model_obj) . md5($func_name);
		if( null != $func_args )$cache_id .= md5(json_encode($func_args));
		if( $cache_file = spAccess('r', "sp_cache_{$cache_id}") )return unserialize( $cache_file );
		return $this->cache_obj($cache_id, call_user_func_array(array($this->model_obj, $func_name), $func_args), $this->input_args[0]);
	}
	/**
	 * 执行spModel子类对象的方法，并对返回结果进行缓存。
	 *
	 * @param obj    引用的spModel子类对象
	 * @param func_name    需要执行的函数名称
	 * @param func_args    函数的参数
	 * @param life_time    缓存生存时间
	 */
	public function cache_obj($cache_id, $run_result, $life_time = null ){
		if( null == $life_time )$life_time = $this->life_time;
		spAccess('w', "sp_cache_{$cache_id}", serialize($run_result), $life_time);
		if( $cache_list = spAccess('r', 'sp_cache_list') ){
			$cache_list = explode("\n",$cache_list);
			if( ! in_array( $cache_id, $cache_list ) )spAccess('w', 'sp_cache_list', join("\n", $cache_list) . $cache_id . "\n");
		}else{
			spAccess('w', 'sp_cache_list', $cache_id . "\n");
		}
		return $run_result;
	}
	/**
	 * 清除单个函数缓存的数据
	 *
	 * @param obj    引用的spModel子类对象
	 * @param func_name    需要执行的函数名称
	 * @param func_args    函数的参数，在默认不输入参数的情况下，将清除全部该函数生成的缓存。
	 * 如果func_args有设置，将只会清除该参数产生的缓存。
	 */
	public function clear(& $obj, $func_name, $func_args = null){
		$cache_id = get_class($obj) . md5($func_name);
		if( null != $func_args )$cache_id .= md5(json_encode($func_args));
		if( $cache_list = spAccess('r', 'sp_cache_list') ){
			$cache_list = explode("\n",$cache_list);
			$new_list = '';
			foreach( $cache_list as $single_item ){
				if( $single_item == $cache_id || ( null == $func_args && substr($single_item,0,strlen($cache_id)) == $cache_id ) ){
					spAccess('c', "sp_cache_{$single_item}");
				}else{
					$new_list .= $single_item. "\n";
				}
			}
			spAccess('w', 'sp_cache_list', substr($new_list,0,-1));
		}
		return TRUE;
	}
	/**
	 * 清除全部函数缓存的数据
	 *
	 */
	public function clear_all(){
		if( $cache_list = spAccess('r', 'sp_cache_list') ){
			$cache_list = explode("\n",$cache_list);
			foreach( $cache_list as $single_item )spAccess('c', "sp_cache_{$single_item}");
			spAccess('c', 'sp_cache_list');
		}
		return TRUE;
	}
}

/**
 * spLinker
 * 数据库的表间关联程序
 */
class spLinker
{
	/**
	 * 模型对象
	 */
	private $model_obj = null;

	/**
	 * 预准备的结果
	 */
	private $prepare_result = null;

	/**
	 * 运行的结果
	 */
	private $run_result = null;

	/**
	 * 可支持的关联方法
	 */
	private $methods = array('find','findBy','findAll','run','create','delete','deleteByPk','update');
	/**
	 * 是否启用全部关联
	 */
	public $enabled = TRUE;
	/**
	 * 函数式使用模型辅助类的输入函数
	 */
    public function __input(& $obj, $args = null){
		$this->model_obj = $obj;
		return $this;
	}

	/**
	 * 开发者可以通过spLinker()->fun($result)对已经返回的数据进行关联findAll查找
	 * @param result    返回的数据
	 */
    public function run($result = FALSE){
    	if( FALSE == $result )return FALSE;
		$this->run_result = $result;
		return $this->__call('run', null);
	}

	/**
	 * 魔术函数，支持多重函数式使用类的方法
	 *
	 * 在spLinker类中，__call执行了spModel继承类的相关操作，以及按关联的描述进行了对关联数据模型类的操作。
	 */
	public function __call($func_name, $func_args){
		if( in_array( $func_name, $this->methods ) && FALSE != $this->enabled ){
			if( 'delete' == $func_name || 'deleteByPk' == $func_name )$maprecords = $this->prepare_delete($func_name, $func_args);
			if( null != $this->run_result ){
				$run_result = $this->run_result;
			}elseif( !$run_result = call_user_func_array(array($this->model_obj, $func_name), $func_args) ){
				if( 'update' != $func_name )return FALSE;
			}
			if( null != $this->model_obj->linker && is_array($this->model_obj->linker) ){
				foreach( $this->model_obj->linker as $linkey => $thelinker ){
					if( !isset($thelinker['map']) )$thelinker['map'] = $linkey;
					if( FALSE == $thelinker['enabled'] )continue;
					$thelinker['type'] = strtolower($thelinker['type']);
					if( 'find' == $func_name || 'findBy' == $func_name ){
						$run_result[$thelinker['map']] = $this->do_select( $thelinker, $run_result );
					}elseif( 'findAll' == $func_name || 'run' == $func_name ){
						foreach( $run_result as $single_key => $single_result )
							$run_result[$single_key][$thelinker['map']] = $this->do_select( $thelinker, $single_result );
					}elseif( 'create' == $func_name ){
						$this->do_create( $thelinker, $run_result, $func_args );
					}elseif( 'update' == $func_name ){
						$this->do_update( $thelinker, $func_args );
					}elseif( 'delete' == $func_name || 'deleteByPk' == $func_name ){
						$this->do_delete( $thelinker, $maprecords );
					}
				}
			}
			return $run_result;
		}elseif(in_array($func_name, $GLOBALS['G_SP']["auto_load_model"])){
			return spClass($func_name)->__input($this, $func_args);
  		}else{
			return call_user_func_array(array($this->model_obj, $func_name), $func_args);
		}
	}

	/**
	 * 私有函数，辅助删除数据操作
	 * @param func_name    需要执行的函数名称
	 * @param func_args    函数的参数
	 */
	private function prepare_delete($func_name, $func_args)
	{
		if('deleteByPk'==$func_name){
			return $this->model_obj->findAll(array($this->model_obj->pk=>$func_args[0]));
		}else{
			return $this->model_obj->findAll($func_args[0]);
		}
	}
	/**
	 * 私有函数，进行关联删除数据操作
	 * @param thelinker    关联的描述
	 * @param maprecords    对应的记录
	 */
	private function do_delete( $thelinker, $maprecords ){
		if( FALSE == $maprecords )return FALSE;
		foreach( $maprecords as $singlerecord ){
			if(!empty($thelinker['condition'])){
				if( is_array($thelinker['condition']) ){
					$fcondition = array($thelinker['fkey']=>$singlerecord[$thelinker['mapkey']]) + $thelinker['condition'];
				}else{
					$fcondition = "{$thelinker['fkey']} = '{$singlerecord[$thelinker['mapkey']]}' AND {$thelinker['condition']}";
				}
			}else{
				$fcondition = array($thelinker['fkey']=>$singlerecord[$thelinker['mapkey']]);
			}
			$returns = spClass($thelinker['fclass'])->delete($fcondition);
		}
		return $returns;
	}
	/**
	 * 私有函数，进行关联更新数据操作
	 * @param thelinker    关联的描述
	 * @param func_args    进行操作的参数
	 */
	private function do_update( $thelinker, $func_args ){
		if( !is_array($func_args[1][$thelinker['map']]) )return FALSE;
		if( !$maprecords = $this->model_obj->findAll($func_args[0]))return FALSE;
		foreach( $maprecords as $singlerecord ){
			if(!empty($thelinker['condition'])){
				if( is_array($thelinker['condition']) ){
					$fcondition = array($thelinker['fkey']=>$singlerecord[$thelinker['mapkey']]) + $thelinker['condition'];
				}else{
					$fcondition = "{$thelinker['fkey']} = '{$singlerecord[$thelinker['mapkey']]}' AND {$thelinker['condition']}";
				}
			}else{
				$fcondition = array($thelinker['fkey']=>$singlerecord[$thelinker['mapkey']]);
			}
			$returns = spClass($thelinker['fclass'])->update($fcondition, $func_args[1][$thelinker['map']]);
		}
		return $returns;
	}
	/**
	 * 私有函数，进行关联新增数据操作
	 * @param thelinker    关联的描述
	 * @param newid    主表新增记录后的关联ID
	 * @param func_args    进行操作的参数
	 */
	private function do_create( $thelinker, $newid, $func_args ){
		if( !is_array($func_args[0][$thelinker['map']]) )return FALSE;
		if('hasone'==$thelinker['type']){
			$newrows = $func_args[0][$thelinker['map']];
			$newrows[$thelinker['fkey']] = $newid;
			return spClass($thelinker['fclass'])->create($newrows);
		}elseif('hasmany'==$thelinker['type']){
			if(array_key_exists(0,$func_args[0][$thelinker['map']])){ // 多个新增
				foreach($func_args[0][$thelinker['map']] as $singlerows){
					$newrows = $singlerows;
					$newrows[$thelinker['fkey']] = $newid;
					$returns = spClass($thelinker['fclass'])->create($newrows);
				}
				return $returns;
			}else{ // 单个新增
				$newrows = $func_args[0][$thelinker['map']];
				$newrows[$thelinker['fkey']] = $newid;
				return spClass($thelinker['fclass'])->create($newrows);
			}
		}
	}

	/**
	 * 私有函数，进行关联查找数据操作
	 * @param thelinker    关联的描述
	 * @param run_result    主表执行查找后返回的结果
	 */
	private function do_select( $thelinker, $run_result ){
		if(empty($thelinker['mapkey']))$thelinker['mapkey'] = $this->model_obj->pk;
		if( 'manytomany' == $thelinker['type'] ){
			$do_func = 'findAll';
			$midcondition = array($thelinker['mapkey']=>$run_result[$thelinker['mapkey']]);
			if( !$midresult = spClass($thelinker['midclass'])->findAll($midcondition,null,$thelinker['fkey']) )return FALSE;
			$tmpkeys = array();foreach( $midresult as $val )$tmpkeys[] = "'".$val[$thelinker['fkey']]."'";
			if(!empty($thelinker['condition'])){
				if( is_array($thelinker['condition']) ){
					$fcondition = "{$thelinker['fkey']} in (".join(',',$tmpkeys).")";
					foreach( $thelinker['condition'] as $tmpkey => $tmpvalue )$fcondition .= " AND {$tmpkey} = '{$tmpvalue}'";
				}else{
					$fcondition = "{$thelinker['fkey']} in (".join(',',$tmpkeys).") AND {$thelinker['condition']}";
				}
			}else{
				$fcondition = "{$thelinker['fkey']} in (".join(',',$tmpkeys).")";
			}
		}else{
			$do_func = ( 'hasone' == $thelinker['type'] ) ? 'find' : 'findAll';
			if(!empty($thelinker['condition'])){
				if( is_array($thelinker['condition']) ){
					$fcondition = array($thelinker['fkey']=>$run_result[$thelinker['mapkey']]) + $thelinker['condition'];
				}else{
					$fcondition = "{$thelinker['fkey']} = '{$run_result[$thelinker['mapkey']]}' AND {$thelinker['condition']}";
				}
			}else{
				$fcondition = array($thelinker['fkey']=>$run_result[$thelinker['mapkey']]);
			}
		}
		if(TRUE == $thelinker['countonly'])$do_func = "findCount";
		return spClass($thelinker['fclass'])->$do_func($fcondition, $thelinker['sort'], $thelinker['field'], $thelinker['limit'] );
	}
}
