<?php
/*
 * database2/AbstractConnection.class.php
 * 
 * CopyRight(C) 2010 Shopformer Development Team. All Right Reserved
 * URL: http://sourceforge.jp/projects/shopformer/
 * 
 * 
 * Mail: m_nakashima@users.sourceforge.jp
 * Auther: Masanori Nakashima
 * Last Update: 2010-06-18
 */
require_once(dirname(__FILE__)
.DIRECTORY_SEPARATOR.'AbstractData.class.php');
/**
 * 単一のデータベースコネクションを保持するデータベース接続の抽象クラスです。
 */
class database2_AbstractConnection {

	/** spider_HttpRequestオブジェクトへの参照	*/
	var $request		= null;
	/** ＤＢリソース	*/
	var $resource_id	= null;
	/** データベース文字コード	*/
	var $db_encoding	= null;
	/** 取り出し文字コード	*/
	var $encoding		= 'UTF-8';
	/** queryAllのlimit	*/
	var $limit				= null;
	/** queryAllのoffset	*/
	var $offset				= null;
	
	/**
	 * setRequest
	 */
	function _setHttpRequest( & $request ) {
		$this->request	= $request;
	}
	/**
	 * データベースに接続します
	 * @return boolean
	 * @access public
	 */
	function _connect( $databaseName, $databaseUser, $databasePass, $host=null, $port=null, $dbencoding='auto' ) {
		return false;
	}
	/**
	 * データベース切断します
	 * @param boolean $force
	 * @return 
	 * @access public
	 */
	function _disconnect( $force = true ) {
		return false;
	}
	/**
	 * 文字列をquoteします
	 * @param string $value 文字列
	 * @param string $type カラム型
	 * @param boolean $quote quoteする場合はtrue、しない場合はfalse
	 * @param boolean $escape_wildcards ワイルドカードをquoteする場合はtrue、しない場合はfalse
	 * @return string quoteした文字列
	 * @access public
	 */
	function _quote( $value, $type = null, $quote = true, $escape_wildcards = false ) {
		return false;
	}
	/**
	 * 文字列をエスケープします
	 * @param string $text
	 * @param boolean $escape_wildcards
	 * @return 
	 * @access public
	 */
	function _escape($text, $escape_wildcards = false){
		return false;
	}
	/**
	 * Transactionを開始します
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function _beginTransaction( $savepoint = null ) {
		return false;
	}
	/**
	 * トランザクションをコミットします
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function _commit( $savepoint = null ) {
		return false;
	}
	/**
	 * トランザクションをロールバックします
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function _rollback( $savepoint = null ) {
		return false;
	}
	/**
	 * 問い合わせ結果レコードの行数とオフセットを設定します
	 * @param string $limit
	 * @param string $offset
	 * @return 
	 * @access public
	 */
	function _setLimit( $limit, $offset = null ) {
		$this->limit	= $limit;
		$this->offset	= $offset;
	}
	/**
	 * 渡されたSQLクエリ文を実行します
	 * @param string $query SQLクエリ
	 * @return 
	 * @access public
	 */
	function _query( $query ) {
		return false;
	}
	/**
	 * 渡されたSQLクエリ文を実行して全ての結果を取得します
	 * @param string $query SQLクエリ
	 * @param mixed $fetchtype 使用する取得モード。0=ハッシュ, 1=配列, AbstractDataの拡張クラス=オブジェクト配列
	 * @return mixed 入れ子状の配列、あるいは失敗した場合にMDB_Errorを返します。
	 * @access public
	 */
	function _queryAll( $query, $fetchtype=null, $setOrg=true ) {
		return false;
	}
	/**
	 * 渡されたSQLクエリ文を実行して1レコードのみ結果を取得します
	 * レコードがなかった場合または、1レコードに絞り込めなかった場合はfalseを返します
	 * @param string $query SQLクエリ
	 * @param mixed $fetchtype 使用する取得モード。0=ハッシュ, 1=配列, AbstractDataの拡張クラス=オブジェクト
	 * @return 
	 * @access public
	 */
	function _queryRow( $query, $fetchtype=null, $setOrg=true ) {
		return false;
	}
	/**
	 * 渡されたSQLクエリ文を実行して1カラム分のみ結果を取得します
	 * @param string $query SQLクエリ
	 * @return 
	 * @access public
	 */
	function _queryOne( $query ) {
		return false;
	}
	/**
	 * ハッシュに格納された一行のデータをオブジェクトメンバ値に格納します。
	 */
	function _setRowToFields( $row, & $daoBase, $setOrg=true, $setDivided=true ) {
		if( is_array($row) ) {
			if( !is_array($daoBase->baseUniqueKeyFiledNames) ) {
				$this->_setUniqueFieldInfo( $daoBase );
			}
			foreach( $row as $key=>$val ) {
				if( is_array($daoBase->baseUniqueKeyFiledNames) && in_array( $key, $daoBase->baseUniqueKeyFiledNames ) ) {
					$val	= trim($val);
				}
				$daoBase->$key = $val;
				if( $setOrg ) {
					$fname	= $key . '_org';
					$daoBase->$fname = $val;
				}
				// 日付フィールド・時間フィールド・日時フィールドの処理
				if( $setDivided ){
					if ( preg_match( '/^[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}$/', $val ) ) {
						// DATE型
						$name_year	= $key.'_year';
						$name_month	= $key.'_month';
						$name_day	= $key.'_day';
						list( $daoBase->$name_year, $daoBase->$name_month, $daoBase->$name_day )
							= explode( '-', $val );
					} else if ( preg_match( '/^[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $val )
						|| preg_match( '/^[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+$/', $val ) ) {
						// TIME型
						$name_hour	= $key.'_hour';
						$name_min	= $key.'_min';
						$name_sec	= $key.'_sec';
						list( $daoBase->$name_hour, $daoBase->$name_min, $daoBase->$name_sec )
							= explode( ':', $val );
					} else if ( preg_match( '/^[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $val )
						|| preg_match( '/^[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]+$/', $val ) ) {
						list( $date_str, $time_str )	= explode(' ', $val );
						$name_year	= $key.'_year';
						$name_month	= $key.'_month';
						$name_day	= $key.'_day';
						list( $daoBase->$name_year, $daoBase->$name_month, $daoBase->$name_day )
							= explode( '-', $date_str );
						$name_hour	= $key.'_hour';
						$name_min	= $key.'_min';
						$name_sec	= $key.'_sec';
						list( $daoBase->$name_hour, $daoBase->$name_min, $daoBase->$name_sec )
							= explode( ':', $time_str );
					}
				}
			}
			return true;
		} else {
			return false;
		}
	}
	/**
	 * データアクセスオブジェクトにフィールドキー情報のリファレンスを設定します
	 */
	function _setUniqueFieldInfo( & $daoBase ) {
		$baseSerialFieldName		= $this->_getInformationPool( 'tableCache', 'serial', $daoBase->getTableName() );
		$baseUniqueKeyFiledNames	= $this->_getInformationPool( 'tableCache', 'unique', $daoBase->getTableName() );
		if( is_null($baseSerialFieldName) || is_null($baseSerialFieldName) ) {
			$baseSerialFieldName		= null;
			$baseUniqueKeyFiledNames	= array();
			$tableInformationHash		= $this->getTableInformationHash( $daoBase );
			if( false !== $tableInformationHash ) {
				foreach ( $tableInformationHash as $fieldName => $fieldInformation ) {
					if( isset($fieldInformation['key']) && preg_match('/[uU][nN][iI][qQ][uU][eE]/',$fieldInformation['key']) > 0 ) {
						// ユニークキーなら
						array_push( $baseUniqueKeyFiledNames, $fieldName );
					} else if( isset($fieldInformation['is_serial']) && $fieldInformation['is_serial'] ) {
						// シリアル番号なら
						$baseSerialFieldName	= $fieldName;
					}
				}
				$this->_setInformationPool( 'tableCache', 'serial', $daoBase->getTableName(), $baseSerialFieldName );
				$this->_setInformationPool( 'tableCache', 'unique', $daoBase->getTableName(), $baseUniqueKeyFiledNames );
				$baseSerialFieldName		= $this->_getInformationPool( 'tableCache', 'serial', $daoBase->getTableName() );
				$baseUniqueKeyFiledNames	= $this->_getInformationPool( 'tableCache', 'unique', $daoBase->getTableName() );
			} else {
				return false;
			}
		}
		$daoBase->baseSerialFieldName		= $baseSerialFieldName;
		$daoBase->baseUniqueKeyFiledNames	= $baseUniqueKeyFiledNames;
		return true;
	}
	/**
	 * 渡されたデータ抽象オブジェクトに該当するテーブル情報ハッシュを取得します。
	 * 【テーブル情報ハッシュの構造】
	 * array( [フィールド名] => [フィールド詳細情報ハッシュ] )
	 * 
	 * 【フィールド詳細情報ハッシュの構造】
	 * array(
	 * 		'type'			=> 'int/char/varchar/text...',
	 * 		'size'			=> 数値
	 * 		'not_null'		=> true/false
	 * 		'key'			=> 'primary/unique'
	 * 		'is_serial'		=> true/false
	 * 		'has_default'	=> true/false
	 * )
	 */
	function getTableInformationHash( $daoBase ) {
		return $this->getTableInformationHashByName( $daoBase->getTableName() );
	}
	/**
	 * テーブル名称を指定して該当するテーブル情報ハッシュを取得します。
	 * 【テーブル情報ハッシュの構造】
	 * array( [フィールド名] => [フィールド詳細情報ハッシュ] )
	 * 
	 * 【フィールド詳細情報ハッシュの構造】
	 * array(
	 * 		'type'			=> 'int/char/varchar/text...',
	 * 		'size'			=> 数値
	 * 		'not_null'		=> true/false
	 * 		'key'			=> 'primary/unique'
	 * 		'is_serial'		=> true/false
	 * 		'has_default'	=> true/false
	 * )
	 */
	function getTableInformationHashByName( $tableName ) {
		$infoHash	= false;
		if( !is_null($infoHash = $this->_getInformationPool( 'tableCache', 'tableInformation', $tableName ) ) ) {
			$ref	= & $infoHash;
			return $ref;
		}
		$cachePath	= $this->_getTableInformationCachePath( $tableName );
		if( file_exists($cachePath) && filemtime($cachePath) > time() - (60*60*24) ) {
			// 一日以内のキャッシュがあるならキャッシュを利用
			$str		= file_get_contents($cachePath);
			$infoHash	= unserialize($str);
		} else {
			// キャッシュがないなら問い合わせ
			$infoHash	= $this->_getTableInformationHashByName( $tableName );
			// キャッシュを作成
			$fp		= @fopen($cachePath,'w');
			if( $fp ) {
				if (@flock($fp, LOCK_EX)) {
					@fwrite( $fp, serialize($infoHash) );
					@flock($fp, LOCK_UN);
				}
				@fclose( $fp );
				@chmod( $cachePath, SPIDER_PERMITTION_DATA_FILE );
			}
		}
		// グローバル変数にプール
		$this->_setInformationPool( 'tableCache', 'tableInformation', $tableName, $infoHash );
		$ref	= & $infoHash;
		return $ref;
	}
	/**
	 * 渡されたデータ抽象オブジェクトに該当するテーブル情報ハッシュを取得します。
	 * 【テーブル情報ハッシュの構造】
	 * array( [フィールド名] => [フィールド詳細情報ハッシュ] )
	 * 
	 * 【フィールド詳細情報ハッシュの構造】
	 * array(
	 * 		'type'			=> 'int/char/varchar/text...',
	 * 		'size'			=> 数値
	 * 		'not_null'		=> true/false
	 * 		'key'			=> 'primary/unique'
	 * 		'is_serial'		=> true/false
	 * 		'has_default'	=> true/false
	 * )
	 */
	function _getTableInformationHash( $daoBase ) {
		$tableName	= $daoBase->getTableName();
		return $this->_getTableInformationHashByName( $tableName );
	}
	/**
	 * テーブル名称を指定して該当するテーブル情報ハッシュを取得します。
	 * 各ＤＢへの接続実装クラスでオーバーライドする必要があります。
	 * 【テーブル情報ハッシュの構造】
	 * array( [フィールド名] => [フィールド詳細情報ハッシュ] )
	 * 
	 * 【フィールド詳細情報ハッシュの構造】
	 * array(
	 * 		'type'			=> 'int/char/varchar/text...',
	 * 		'size'			=> 数値
	 * 		'not_null'		=> true/false
	 * 		'key'			=> 'primary/unique'
	 * 		'is_serial'		=> true/false
	 * 		'has_default'	=> true/false
	 * )
	 */
	function _getTableInformationHashByName( $tableName ) {
		return false;
	}
	/**
	 * 指定名称のデータベーステーブルが存在するか確認します
	 */
	function _existsTable( $tableName ) {
		return false;
	}
	/**
	 * 指定名称のデータベースが存在するか確認します
	 */
	function _existsDatabase( $databaseName ) {
		return false;
	}
	/**
	 * 指定データベースのエンコード設定を取得します。
	 */
	function _selectDatabaseEncoding($databaseName) {
		return false;
	}
	/**
	 * データベースに問い合わせた情報プールからデータを取得します。
	 */
	function _getInformationPool( $class, $name, $key, $reference=true ) {
		$poolKey	= 'database2_connection_data_pool';
		if( !isset($GLOBALS[$poolKey]) || !is_array($GLOBALS[$poolKey]) ) {
			return null;
		}
		if( !isset($GLOBALS[$poolKey][$class]) || !is_array($GLOBALS[$poolKey][$class]) ) {
			return null;
		}
		if( !isset($GLOBALS[$poolKey][$class][$name]) || !is_array($GLOBALS[$poolKey][$class][$name]) ) {
			return null;
		}
		if( !isset($GLOBALS[$poolKey][$class][$name][$key]) ) {
			return null;
		} else {
			if( $reference ) {
				$ref	= & $GLOBALS[$poolKey][$class][$name][$key];
				return $ref;
			} else {
				return $GLOBALS[$poolKey][$class][$name][$key];
			}
		}
	}
	/**
	 * データベースに問い合わせた情報を一つの接続で何度も問い合わせない為にグローバル変数にプールします。
	 */
	function _setInformationPool( $class, $name, $key, $values ) {
		$poolKey	= 'database2_connection_data_pool';
		if( !isset($GLOBALS[$poolKey]) || !is_array($GLOBALS[$poolKey]) ) {
			$GLOBALS[$poolKey]	= array();
		}
		if( !isset($GLOBALS[$poolKey][$class]) || !is_array($GLOBALS[$poolKey][$class]) ) {
			$GLOBALS[$poolKey][$class]	= array();
		}
		if( !isset($GLOBALS[$poolKey][$class][$name]) || !is_array($GLOBALS[$poolKey][$class][$name]) ) {
			$GLOBALS[$poolKey][$class][$name]	= array();
		}
		$GLOBALS[$poolKey][$class][$name][$key]	= $values;
	}
	/**
	 * テーブル情報キャッシュファイルパスを取得します
	 */
	function _getTableInformationCachePath( $tableName ) {
		$path	= DIR_PATH_CACHE.DIRECTORY_SEPARATOR.get_class($this);
		if( !file_exists($path) ) {
			if( @mkdir($path,SPIDER_PERMITTION_DATA_FOLDER) ) {
				@chmod($path,SPIDER_PERMITTION_DATA_FOLDER);
			}
		}
		$path	.= DIRECTORY_SEPARATOR.'tables';
		if( !file_exists($path) ) {
			if( @mkdir($path,SPIDER_PERMITTION_DATA_FOLDER) ) {
				@chmod($path,SPIDER_PERMITTION_DATA_FOLDER);
			}
		}
		$path	.= DIRECTORY_SEPARATOR.$tableName;
		return $path;
	}
	/**
	 * Fatalレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function fatal($message){
		if( !is_null($this->request) ) {
			$this->request->fatal($message);
		}
	}
	/**
	 * Errorレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function error($message){
		if( !is_null($this->request) ) {
			$this->request->error($message);
		}
	}
	/**
	 * Warningレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function warn($message){
		if( !is_null($this->request) ) {
			$this->request->warn($message);
		}
	}
	/**
	 * Noticeレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function notice($message){
		if( !is_null($this->request) ) {
			$this->request->notice($message);
		}
	}
	/**
	 * Infoレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function info($message){
		if( !is_null($this->request) ) {
			$this->request->info($message);
		}
	}
	/**
	 * Debugレベルのログを出力します。
	 * @param $message ログメッセージ
	 */
	function debug($message){
		if( !is_null($this->request) ) {
			$this->request->debug($message);
		}
	}
}
?>