﻿<?php
/////////////////////////////////////////////////////////////////////////////
//
// SpeedPHP - 蹇€熺殑涓枃PHP妗嗘灦
//
// Copyright (c) 2008 - 2009 SpeedPHP.com All rights reserved.
//
// 璁稿彲鍗忚璇锋煡鐪?http://www.speedphp.com/
//
/////////////////////////////////////////////////////////////////////////////

/**
 * spModel 绯荤粺妯″瀷绫伙紝镓€链夋ā鍨嬬被镄勭埗绫?搴旗敤绋嫔簭涓殑姣忎釜妯″瀷绫婚兘搴旗户镓夸簬spModel銆?
 */
class spModel {
	/**
	 * 渚涙楠屽€肩殑瑙勫垯涓庤繑锲炰俊鎭?
	 */
	public $verifier = null;
	
	/**
	 * 澧炲姞镄勮嚜瀹氢箟楠岃瘉鍑芥暟
	 */
	public $addrules = array();
	/**
	 * 琛ㄤ富阌?
	 */
	public $pk;
	/**
	 * 琛ㄥ悕绉?
	 */
	public $table;

	/**
	 * 鍏宠仈鎻忚堪
	 */
	public $linker = null;
	
	/**
	 * 琛ㄥ叏鍚?
	 */
	protected $tbl_name = null;
	
	/**
	 * 鏁版嵁椹卞姩绋嫔簭
	 */
	protected $_db;

	/**
	 * 鏋勯€犲嚱鏁?
	 */
	public function __construct()
	{
		if( null == $this->tbl_name )$this->tbl_name = $GLOBALS['G_SP']['db']['prefix'] . $this->table;
		$this->_db = spClass($GLOBALS['G_SP']['db']['driver'], $GLOBALS['G_SP']['db'], $GLOBALS['G_SP']['sp_core_path'].$GLOBALS['G_SP']['db_driver_path']);
	}

	/**
	 * 浠庢暟鎹〃涓煡镓句竴鏉¤褰?
	 *
	 * @param conditions    镆ユ垒鏉′欢锛屾暟缁刟rray("瀛楁鍚?=>"镆ユ垒链?)鎴栧瓧绗︿覆锛?
	 * 璇锋敞镒忓湪浣跨敤瀛楃涓叉椂灏嗛渶瑕佸紑鍙戣€呰嚜琛屼娇鐢╛_val_escape鏉ュ杈揿叆链艰繘琛岃绷婊?
	 * @param sort    鎺掑簭锛岀瓑鍚屼簬钬渌RDER 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    镆ユ垒鏉′欢锛屾暟缁刟rray("瀛楁鍚?=>"镆ユ垒链?)鎴栧瓧绗︿覆锛?
	 * 璇锋敞镒忓湪浣跨敤瀛楃涓叉椂灏嗛渶瑕佸紑鍙戣€呰嚜琛屼娇鐢╛_val_escape鏉ュ杈揿叆链艰繘琛岃绷婊?
	 * @param sort    鎺掑簭锛岀瓑鍚屼簬钬渌RDER BY 钬?
	 * @param fields    杩斿洖镄勫瓧娈佃寖锲达紝榛樿涓鸿繑锲炲叏閮ㄥ瓧娈电殑链?
	 * @param limit    杩斿洖镄勭粨鏋沧暟閲忛檺鍒讹紝绛夊悓浜庘€淟IMIT 钬濓紝濡?limit = " 3, 5"锛屽嵆鏄粠绗?鏉¤褰曞紑濮嬭幏鍙栵紝鍏辫幏鍙?鏉¤褰?
	 */
	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->__val_escape($condition);
				$join[] = "{$key} = '{$condition}'";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		if(null != $sort)$sort = "ORDER BY {$sort}";
		if(null != $limit)$limit = "LIMIT {$limit}";
		$sql = "SELECT {$this->tbl_name}.{$fields} FROM {$this->tbl_name} {$where} {$sort} {$limit}";
		return $this->_db->getArray($sql);
	}
	/**
	 * 杩囨护杞箟瀛楃
	 *
	 * @param value 闇€瑕佽繘琛岃绷婊ょ殑链?
	 */
	public function __val_escape($value)
	{
		return $this->_db->__val_escape($value);
	}


	/**
	 * 浣跨敤SQL璇彞杩涜镆ユ垒鎿崭綔锛岀瓑浜庤繘琛宖ind锛宖indAll绛夋搷浣?
	 *
	 * @param sql 瀛楃涓诧紝闇€瑕佽繘琛屾煡镓剧殑SQL璇彞
	 */
	public function findSql($sql)
	{
		return $this->_db->getArray($sql);
	}

	/**
	 * 鍦ㄦ暟鎹〃涓柊澧炰竴琛屾暟鎹?
	 *
	 * @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->__val_escape($value)."'";
		}
		$col = join(',', $cols);
		$val = join(',', $vals);
		
		$sql = "INSERT INTO {$this->tbl_name} ({$col}) VALUES ({$val})";
		if( FALSE != $this->_db->exec($sql) ){ // 銮峰彇褰揿墠鏂板镄処D
			if( $newinserid = $this->_db->newinsertid() ){
				return $newinserid;
			}else{
				return array_pop( $this->find($row, "{$this->pk} DESC",$this->pk) );
			}
		}
		return FALSE;
	}

	/**
	 * 鍦ㄦ暟鎹〃涓柊澧炲鏉¤褰?
	 *
	 * @param rows 鏁扮粍褰㈠纺锛屾疮椤瑰潎涓篶reate镄?row镄勪竴涓暟缁?
	 */
	public function createAll($rows)
	{
		foreach($rows as $row)$this->create($row);
	}

	/**
	 * 鎸夋浔浠跺垹闄よ褰?
	 *
	 * @param conditions 鏁扮粍褰㈠纺锛屾煡镓炬浔浠讹紝姝ゅ弬鏁扮殑镙煎纺鐢ㄦ硶涓巉ind/findAll镄勬煡镓炬浔浠跺弬鏁版槸鐩稿悓镄勩€?
	 */
	public function delete($conditions)
	{
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->__val_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 鏁扮粍褰㈠纺锛屾煡镓炬浔浠讹紝姝ゅ弬鏁扮殑镙煎纺鐢ㄦ硶涓巉ind/findAll镄勬煡镓炬浔浠跺弬鏁版槸鐩稿悓镄勩€?
	 * @param field 瀛楃涓诧紝瀵瑰簲鏁版嵁琛ㄤ腑镄勯渶瑕佷慨鏀圭殑瀛楁鍚?
	 * @param value 瀛楃涓诧紝鏂板€?
	 */
	public function updateField($conditions, $field, $value)
	{
		return $this->update($conditions, array($field=>$value));
	}

	/**
	 * 镓цSQL璇彞锛岀浉绛変簬镓ц鏂板锛屼慨鏀癸紝鍒犻櫎绛夋搷浣溿€?
	 *
	 * @param sql 瀛楃涓诧紝闇€瑕佹墽琛岀殑SQL璇彞
	 */
	public function query($sql)
	{
		return $this->_db->exec($sql);
	}

	/**
	 * 杩斿洖链€鍚庢墽琛岀殑SQL璇彞渚涘垎鏋?
	 */
	public function dumpSql()
	{
		return end( $this->_db->arrSql );
	}

	/**
	 * 璁＄畻绗﹀悎鏉′欢镄勮褰曟暟閲?
	 *
	 * @param conditions 镆ユ垒鏉′欢锛屾暟缁刟rray("瀛楁鍚?=>"镆ユ垒链?)鎴栧瓧绗︿覆锛?
	 * 璇锋敞镒忓湪浣跨敤瀛楃涓叉椂灏嗛渶瑕佸紑鍙戣€呰嚜琛屼娇鐢╛_val_escape鏉ュ杈揿叆链艰繘琛岃绷婊?
	 */
	public function findCount($conditions = null)
	{
		$where = "";
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $condition ){
				$condition = $this->__val_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("method {$name} not defined");
		}
	}

	/**
	 * 淇敼鏁版嵁锛岃鍑芥暟灏嗘抵鎹弬鏁颁腑璁剧疆镄勬浔浠惰€屾洿鏂拌〃涓暟鎹?
	 * 
	 * @param conditions    鏁扮粍褰㈠纺锛屾煡镓炬浔浠讹紝姝ゅ弬鏁扮殑镙煎纺鐢ㄦ硶涓巉ind/findAll镄勬煡镓炬浔浠跺弬鏁版槸鐩稿悓镄勩€?
	 * @param row    鏁扮粍褰㈠纺锛屼慨鏀圭殑鏁版嵁锛?
	 *  姝ゅ弬鏁扮殑镙煎纺鐢ㄦ硶涓巆reate镄?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->__val_escape($condition);
				$join[] = "{$key} = '{$condition}'";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
		foreach($row as $key => $value){
			$value = $this->__val_escape($value);
			$vals[] = "{$key} = '{$value}'";
		}
		$values = join(", ",$vals);
		$sql = "UPDATE {$this->tbl_name} SET {$values} {$where}";
		return $this->_db->exec($sql);
	}

	/**
	 * 鎸夌粰瀹氱殑鏁版嵁琛ㄧ殑涓婚敭鍒犻櫎璁板綍
	 *
	 * @param pk    瀛楃涓叉垨鏁板瓧锛屾暟鎹〃涓婚敭镄勫€笺€?
	 */
	public function deleteByPk($pk)
	{
		return $this->delete(array($this->pk=>intval($pk)));
	}

	/**
	 * 鎸夎〃瀛楁璋冩暣阃傚悎镄勫瓧娈?
	 * @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;
	}
	
	/** 
	 * 鐢熸垚鍒嗛〉鏁版嵁
	 */
	private function runpager($func_name, $func_args){
		$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 .= " LIMIT {$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("no verifier rules!");
		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("unkown rules");
				}
				$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 === ( isset($val) && !empty($val) && "" != $val );}
	/** 
	 * 鍐呯疆楠岃瘉鍣紝妫€镆ュ瓧绗︿覆鏄惁灏忎簬鎸囧畾闀垮害
	 * @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);
		}
		return $this->cache_obj( $this->model_obj , $func_name, $func_args, $this->input_args[0]);
	}
	/** 
	 * 镓цspModel瀛愮被瀵硅薄镄勬柟娉曪紝骞跺杩斿洖缁撴灉杩涜缂揿瓨銆?
	 *
	 * @param obj    寮旷敤镄剆pModel瀛愮被瀵硅薄
	 * @param func_name    闇€瑕佹墽琛岀殑鍑芥暟鍚岖О
	 * @param func_args    鍑芥暟镄勫弬鏁?
	 * @param life_time    缂揿瓨鐢熷瓨镞堕棿
	 */
	public function cache_obj(& $obj, $func_name, $func_args = null, $life_time = null ){
		$cache_id = get_class($obj) . md5($func_name);
		if( null != $func_args )$cache_id .= md5(serialize($func_args));
		if( $cache_file = spAccess('r', "sp_cache_{$cache_id}") ){
			return unserialize( $cache_file );
		}
		if( null == $life_time ){
			$life_time = $this->life_time;
		}
		$run_result = call_user_func_array(array($obj, $func_name), $func_args);
		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    寮旷敤镄剆pModel瀛愮被瀵硅薄
	 * @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(serialize($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 $linker = 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->linker = ((null != $args) ? $args[0] : array()) + ((null != $obj->linker) ? $obj->linker : array());
		if( !is_array($this->linker) or empty($this->linker) or (null != $args && FALSE == $args[0]) )$this->enabled = FALSE;
		$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);
	}
	
	/** 
	 * 榄旀湳鍑芥暟锛屾敮鎸佸閲嶅嚱鏁板纺浣跨敤绫荤殑鏂规硶
	 *
	 * 鍦╯pLinker绫讳腑锛宊_call镓ц浜唖pModel缁ф圹绫荤殑鐩稿叧鎿崭綔锛屼互鍙婃寜鍏宠仈镄勬弿杩拌繘琛屼简瀵瑰叧鑱旀暟鎹ā鍨嬬被镄勬搷浣溿€?
	 */
	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->linker ){
				foreach( $this->linker as $thelinker ){
					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;
		}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( FALSE == $thelinker['enabled'] )return FALSE;
		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'] );
	}
}
