<?php
/**
* PEAR:DBデータソースコンポーネント
*
* PHP version 5
*
* @package    components.db
* @author     stk2k <stk2k@sazysoft.com>
* @copyright  2008 stk2k, sazysoft
*/
require_once( 'DB.php' );

class charcoal_PearDbDataSource implements charcoal_IDataSource
{
	var $_connected = false;
	var $_connection;

	var $_backend;
	var $_user;
	var $_password;
	var $_db_name;
	var $_server;
	var $_charset;

	/*
	 *    コンストラクタ
	 */
	public function __construct()
	{
		$this->_connected 	= false;
		$this->_connection 	= null;
	}

	/*
	 * データソース名を取得
	 */
	public function getDataSourceName()
	{
		return "Pear:DB DataSource";
	}

	/*
	 * コンポーネントを初期化する
	 */
	public function init( charcoal_Config $config )
	{
		$this->_backend   = $config->getString( 'backend' );
		$this->_user      = $config->getString( 'user' );
		$this->_password  = $config->getString( 'password' );
		$this->_db_name   = $config->getString( 'db_name' );
		$this->_server    = $config->getString( 'server' );
		$this->_charset   = $config->getString( 'charset' );
	}

	/*
	 *    接続済みか
	 */
	public function isConnected()
	{
		return $this->_connected;
	}

	/*
	 *    バックエンドを取得
	 */
	public function getBackend()
	{
		return $this->_backend;
	}

	/*
	 *    コミットを発行
	 */
	public function commit()
	{
		try {
		
			$this->_connection->commit();
			
		} catch (PDOException $e) {
			throw new charcoal_DBException( $e->getMessage() );
		}
	}

	/*
	 *    デフォルトの接続先に接続
	 */
	public function connectDefault()
	{
		$this->connect( $this->_backend, $this->_user, $this->_password, $this->_server, $this->_db_name );
	}

	/*
	 *    接続
	 */
	public function connect( $backend, $user, $password, $server, $db_name )
	{
		// 接続済みなら何もしない
		if ( $this->_connected ){
			return;
		}

		$dbstring = "$backend://$user:$password@$server/$db_name";
		log_info( "sql", "DB接続を行います。接続文字列=[$dbstring]", __FILE__, __LINE__ );

		$DSN = array(
		    'phptype'  => $backend,
		    'username' => $user,
		    'password' => $password,
		    'hostspec' => $server,
		    'database' => $db_name,
		);

		$db = DB::connect( $DSN );
		
		if ( DB::isError($db) ){
			$msg = DB::errorMessage($db) . "/" . $dbstring;
			log_error( 'system', $msg, __FILE__, __LINE__ );
			throw new charcoal_DBException( $msg );
		}
	
		log_info( "sql", "DBに接続しました。", __FILE__, __LINE__ );

		$this->_connection = $db;
		$this->_connected = true;

		// 文字化け対策
		$charset = $this->_charset;
		$db->query( "SET NAMES $charset" );
	}

	/*
	 *    プリペアドステートメントの発行
	 */
	private function _prepareExecute( $sql, $params = NULL )
	{
		if ( !$sql ){
			throw new charcoal_NullPointerException();
		}
//print "SQL:$sql start<BR>";
		$conv = charcoal_EncodingConverter::fromString( 'DB_TO_LOG' );
		log_info( "sql", $sql, __FILE__, __LINE__ );
		if ( $params ){
			charcoal_Logger::writeArray( "i:sql", $params, __FILE__, __LINE__ );
		}

//print "SQL:$sql<BR>";

		for( $i=0; $i<count($params); $i++ ){
			$p1 = $params[$i];
			if ( is_string($p1) ){
				$params[$i] = $p1;
			}
		}

		$stmt = $this->_connection->prepare($sql);
	
		if ( DB::isError($stmt) ){
			$msg = $stmt->getMessage();
			log_error( "system", $msg ,__FILE__,__LINE__ );
			throw new charcoal_DBException( $msg );
		}

		$result = $this->_connection->execute($stmt, $params);
		
		if ( DB::isError($result) ){
			$msg = $result->getMessage();
			log_error( "system", $msg ,__FILE__,__LINE__ );
			throw new charcoal_DBException( $msg );
		}

		return $result;
	}

	/*
	 *    SQLをそのまま発行
	 */
	private function _query( $sql )
	{
		log_info( "sql", $sql, __FILE__, __LINE__ );
	
		$result = $this->_connection->query($sql);
			
		if ( DB::isError($result) ){
			$msg = $result->getMessage();
			throw new charcoal_DBException( $msg );
		}

		return $result;
	}

	/*
	 *    SQLをそのまま発行（結果セットあり）
	 */
	public function query( $sql )
	{
		log_info( "sql", $sql, __FILE__, __LINE__ );

		// 接続処理
		$this->connect( $this->_backend, $this->_user, $this->_password, $this->_server, $this->_db_name );

		// SQLを実行して結果セットを得る
		$resultset = $this->_query( $sql );

		// 結果セットを返却
		return $resultset;
	}

	/*
	 *    SQLをそのまま発行（結果セットなし）
	 */
	public function execute( $sql )
	{
		log_info( "sql", $sql, __FILE__, __LINE__ );

		// 接続処理
		$this->connect( $this->_backend, $this->_user, $this->_password, $this->_server, $this->_db_name );

		// SQLを実行
		$this->_query( $sql );
	}

	/*
	 *    プリペアドステートメントの発行
	 */
	public function prepareExecute( $sql, $params = NULL )
	{
		$result = null;
		
		try {
			// 接続処理
			$this->connect( $this->_backend, $this->_user, $this->_password, $this->_server, $this->_db_name );

			// statementの実行
			$result = $this->_prepareExecute( $sql, $params );
		}
		catch ( Exception $e ) {
			$msg  = "PearDbDataSource#prepareExecute() failed:<BR>";
			$msg .= "[SQL]$sql<BR> [params]" . charcoal_System::arrayToString($params) . "<BR>";
			throw new charcoal_DBException( $msg, $e );
		}

		return $result;
	}

	/*
	 *    実行結果件数取得
	 */
	function numRows( $result )
	{
		return $result->numRows();
	}

	/*
	 *    フェッチ処理（連想配列で返却）
	 */
	public function fetchAssoc( $result )
	{
		if ( $result === NULL ){
			throw new charcoal_NullPointerException();
		}
		if ( !is_object($result) ){
			throw new charcoal_NonObjectException( $result );
		}
		return $result->fetchRow(DB_FETCHMODE_ASSOC);
	}

	/*
	 *    フェッチ処理（配列で返却）
	 */
	public function fetchArray( $result )
	{
		if ( $result === NULL ){
			throw new charcoal_NullPointerException();
		}
		if ( !is_object($result) ){
			throw new charcoal_NonObjectException( $result );
		}
		return $result->fetchRow(DB_FETCHMODE_ORDERED);
	}

	/*
	 *   最後に実行されたAUTO_INCREMENT値を取得
	 */
	public function getLastInsertId()
	{
		$sql  = 'select LAST_INSERT_ID()';

		$result = $this->prepareExecute( $sql, NULL, __FILE__, __LINE__ );

		if ( $row = $this->fetchArray($result) ){
			$val = $row[0];
			return $val;
		}

		return -1;
	}

}
