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

class PearDbDataSource extends CharcoalComponent implements IDataSource
{
	var $_connected = false;
	var $_connection;

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

	/*
	 *    コンストラクタ
	 */
	public function __construct( String $name = NULL )
	{
		parent::__construct(s($name));

		$this->_connected 	= false;
		$this->_connection 	= null;
	}

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

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

	}

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

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

	/*
	 *    接続先サーバを取得
	 */
	public function getServer()
	{
		return $this->_server;
	}

	/*
	 *    接続ユーザ名を取得
	 */
	public function getUser()
	{
		return $this->_user;
	}

	/*
	 *    接続パスワードを取得
	 */
	public function getPassword()
	{
		return $this->_password;
	}

	/*
	 *    接続先データベース名を取得
	 */
	public function getDatabaseName()
	{
		return $this->_db_name;
	}

	/*
	 *    接続時の文字コードを取得
	 */
	public function getCharacterSet()
	{
		return $this->_charset;
	}

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

	/*
	 *    デフォルトの接続先に接続
	 */
	public function connectDefault( $db_name_enabled = TRUE, $force = FALSE )
	{
		$db_name = $db_name_enabled ? $this->_db_name : NULL;
		$this->connect( $this->_backend, $this->_user, $this->_password, $this->_server, s($db_name), $force );
	}

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

		$DSN = array(
		    'phptype'  => "$backend",
		    'username' => "$user",
		    'password' => "$password",
		    'hostspec' => "$server",
		);
		if ( !$db_name->isEmpty() ){
			$DSN['database'] = "$db_name";
		}
		$DSN = v($DSN);

		$db_string = nl2br($DSN->toString());

		log_info( "sql", "DB接続を行います。接続文字列=[$db_string]", __FILE__, __LINE__ );

		$db = DB::connect( $DSN->toArray() );
		
		if ( DB::isError($db) ){
			$msg = DB::errorMessage($db) . " 接続文字列=[$db_string]";
			log_error( 'system', $msg, __FILE__, __LINE__ );
			_throw( new DBException( $msg ) );
		}
	
		log_info( "sql", "DBに接続しました。", __FILE__, __LINE__ );

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

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

	}

	/*
	 *    接続を閉じる
	 */
	public function disconnect()
	{

		// 接続していないなら何もしない
		if ( !$this->_connected ){
			return;
		}

		// 切断
		$this->_connection->disconnect();

		$this->_connected = FALSE;
	}

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

//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 DBException( $msg ) );
		}

		$params = $params ? $params->toArray() : NULL;

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

		return $result;
	}

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

		return $result;
	}

	/*
	 *    SQLをそのまま発行（結果セットあり）
	 */
	public function query( String $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( String $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( String $sql, Vector $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 )
		{
			_catch( $e );

			$msg  = "PearDbDataSource#prepareExecute() failed:<BR>";
			$msg .= "[SQL]$sql<BR> [params]$params<BR>";

			_throw( new DBException( $msg ) );
		}

		return $result;
	}

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

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

	/*
	 *    フェッチ処理（配列で返却）
	 */
	public function fetchArray( $result )
	{
		if ( $result === NULL ){
			_throw( new NullPointerException() );
		}
		if ( !is_object($result) ){
			_throw( new 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;
	}

}

return __FILE__;