<?php
/*
 * database2/connection/Mysql.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(dirname(__FILE__)).DIRECTORY_SEPARATOR.'AbstractConnection.class.php' );
/**
 * データベース接続オブジェクトクラス - MySQL用
 * 
 * 抽象クラスdatabase_Connectionの実装クラスです。
 * MySQLに接続して接続情報を保持し、各種インターフェースを提供します。
 * 
 * @package database データベースパッケージ
 * @subpackage connection データベースコネクションパッケージ
 * @version 2.0.0
 * @author  <m_nakashima@users.sourceforge.jp>
 * @since PHP 4.0
 * @access protected
 * @see database_Connection
 */
$GLOBALS['DEF_DATABASE2_CONNECTION_MYSQL_ENCODINGS']	= array(
	'sjis'		=> 'SJIS-win',
	'ujis'		=> 'EUC-JP',
	'utf8'		=> 'UTF-8',
	'cp932'	=> 'SJIS-win',
	'ucs2'		=> 'UCS-2',
	'eucjpms'	=> 'EUC-JP',
);
class database2_connection_Mysql extends database2_AbstractConnection {
	
	/**
	 * コンストラクタ
	 * 
	 * database_Connectionクラスのget_instanceメソッドでインスタンスを生成して利用します。
	 * 本メソッドで直接インスタンスを生成して利用しないでください。
	 * @access protected
	 */
	function database2_connection_Mysql() {
	}
	/**
	 * データベースに接続します
	 * @return boolean
	 * @access public
	 */
	function _connect( $databaseName, $databaseUser, $databasePass, $host=null, $port=null, $dbencoding='auto' ) {
		if( is_null( $host ) || strlen(trim($host)) == 0 ) {
			$host	= 'localhost';
		}
		if( is_null($port) || strlen(trim($port)) == 0 ) {
			$port	= '3306';
		}
		if( !function_exists('mysql_connect') ) {
			$error	= 'データベースに接続できませんでした。mysql_connect関数が定義されていません。';
			$this->error($error);
			return false;
		} else {
			// @ホスト名を削除
			if( strpos($databaseUser,'@') !== false ) {
				$databaseUser	= substr($databaseUser,0,strpos($databaseUser,'@'));
			}
			if( $this->resource_id = @mysql_connect($host.':'.$port, $databaseUser, $databasePass) ) {
				if( strlen($databaseName) > 0 ) {
					if( @mysql_select_db( $databaseName, $this->resource_id ) ) {
						if( $dbencoding == 'auto' ) {
							$this->db_encoding	= $this->_selectDatabaseEncoding($databaseName);
						} else if( strlen($dbencoding) > 0 ) {
							$this->db_encoding	= $dbencoding;
						}
						$sql	= 'set names \'utf8\'';
						if( false === $this->_query($sql) ) {
							$this->error('接続は成功しましたがデータベースクライアント文字セット変更に失敗しました '.mysql_error($this->resource_id));
							return false;
						}
						return true;
					} else {
						$this->error('データベースに接続できませんでした '.mysql_error($this->resource_id));
						return false;
					}
				} else {
					return false;
				}
			} else {
				$error	= 'データベースサーバに接続できませんでした ';
				$this->error($error);
				return false;
			}
		}
	}
	/**
	 * データベースから切断します
	 * @param boolean $force
	 * @return 
	 * @access public
	 */
	function _disconnect() {
		if( $this->resource_id ) {
			return @mysql_close( $this->resource_id );
		}
		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 ) {
		if( is_null($value) || strlen($value) == 0 ) {
			return 'NULL';
		} else {
//			if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//				$value	= mb_convert_encoding( $value, $this->db_encoding, $this->encoding );
//			}
			$value	= mysql_real_escape_string($value,$this->resource_id);
			if( $escape_wildcards ) {
				$value	= str_replace('%','\\%',$value);
				$value	= str_replace('_','\\_',$value);
			}
			if( $quote ) {
				$value	= "'".$value."'";
			}
			return $value;
		}
	}
	/**
	 * Transactionを開始します
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function _beginTransaction($savepoint = null){
		if ($this->in_transaction) {
			return true;
		}
		$result =& $this->_query('BEGIN');
		$this->in_transaction = true;
		return true;
	}

	/**
	 * トランザクションをコミットします
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function _commit( $savepoint = null ) {
		$result =& $this->_query('COMMIT');
		$this->in_transaction = false;
		return true;
	}
	/**
	 * トランザクションをロールバックします
	 * @param string $savepoint savepoint名
	 * @return 
	 * @access public
	 */
	function _rollback( $savepoint = null ) {
		$result =& $this->_query('ROLLBACK');
		$this->in_transaction = false;
		return true;
	}
	/**
	 * 渡されたSQLクエリ文を実行します
	 * @param string $query SQLクエリ
	 * @return 
	 * @access public
	 */
	function _query( $query ) {
		$result	= @mysql_query($query);
		if( false === $result ) {
			$this->error( mysql_error($this->resource_id).':' . $query );
		}
		return $result;
	}
	/**
	 * 渡されたSQLクエリ文を実行して全ての結果を取得します
	 * @param string $query SQLクエリ
	 * @param mixed $fetchtype 使用する取得モード。0=ハッシュ, 1=配列, AbstractDataの拡張クラス=オブジェクト配列
	 * @return mixed 入れ子状の配列、あるいは失敗した場合にMDB_Errorを返します。
	 * @access public
	 */
	function _queryAll( $query, $fetchtype=null, $setOrg=true, $setDivided=true ) {
		if( preg_match('/^[sS][eE][lL][eE][cC][tT]/',trim($query)) > 0
			|| preg_match('/^[sS][hH][oO][wW]/',trim($query)) > 0 ) {
			// limitの設定
			if( preg_match('/^[0-9]+$/',$this->limit ) > 0 ) {
				// offset
				if( preg_match('/^[0-9]+$/',$this->offset ) == 0 ) {
					$this->offset	= '0';
				}
				$query	.= ' LIMIT ' .$this->offset . ' ,' . $this->limit;
			}
		} else {
			$this->error_message	.= 'queryAllはSELECT文でしか利用できません。';
			$this->error($this->error_message. ' : ' . $query);
			return false;
		}
		$result	= $this->_query( $query );
		if( false === $result ) {
			$this->error(mysql_error($this->resource_id).':'.$query);
			return false;
		} else {
			$this->limit	= null;
			$this->offset	= null;
			if( is_object( $fetchtype ) && ( is_a( $fetchtype, 'database2_AbstractData' ) || is_a( $fetchtype, 'database2_AbstractUser' ) ) ) {
				// Daoオブジェクトが指定されていた場合
				$className		= get_class( $fetchtype );
				$objectArray	= array();
				while ($row = mysql_fetch_assoc($result)) {
//					if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//						mb_convert_variables($this->encoding,$this->db_encoding,$row);
//					}
					$object		= new $className;
					$this->_setRowToFields( $row, $object, $setOrg, $setDivided );
					array_push( $objectArray, $object );
				}
				mysql_free_result( $result );
				return $objectArray;
			} else if( 1 == $fetchtype ) {
				// 配列で取得
				$rows	= array();
				while ($row = mysql_fetch_row($result)) {
//					if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//						mb_convert_variables($this->encoding,$this->db_encoding,$row);
//					}
					array_push( $rows, $row );
				}
				mysql_free_result( $result );
				return $rows;
			} else {
				// デフォルトはハッシュで取得
				$rows	= array();
				while ($row = mysql_fetch_assoc($result)) {
//					if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//						mb_convert_variables($this->encoding,$this->db_encoding,$row);
//					}
					array_push( $rows, $row );
				}
				mysql_free_result( $result );
				return $rows;
			}
		}
	}
	/**
	 * 渡されたSQLクエリ文を実行して1レコードのみ結果を取得します
	 * @param string $query SQLクエリ
	 * @param array $types 指定した場合は、結果セットのカラムの型が取得したものに設定されます。
	 * @param integer $fetchmode 使用する取得モード。
	 * @return 
	 * @access public
	 */
	function _queryRow( $query, $fetchtype=null, $setOrg=true, $setDivided=true ) {
		$result	= $this->_query( $query );
		if( false === $result ) {
			$this->error(mysql_error($this->resource_id).':'.$query);
			return false;
		} else {
			$count = mysql_num_rows( $result );
			if( -1 == $count ) {
				$this->error(mysql_error($this->resource_id).':'.$query);
				return false;
			} else if( 0 == $count ){
				$this->error('該当するデータが存在しませんでした:'.$query);
				return false;
			} else if( 1 == $count ){
				$row				= null;
				if( is_object( $fetchtype ) && ( is_a( $fetchtype, 'database2_AbstractData' ) || is_a( $fetchtype, 'database2_AbstractUser' ) ) ) {
					$row	= mysql_fetch_assoc($result);
//					if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//						mb_convert_variables($this->encoding,$this->db_encoding,$row);
//					}
					// Daoオブジェクトが指定されていた場合
					$className		= get_class( $fetchtype );
					$object			= new $className;
					$this->_setRowToFields( $row, $object, $setOrg, $setDivided );
					return $object;
				} else if( 1 == $fetchtype ) {
					// 配列で取得
					$row	= mysql_fetch_row($result);
//					if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//						mb_convert_variables($this->encoding,$this->db_encoding,$row);
//					}
					mysql_free_result( $result );
					return $row;
				} else {
					// デフォルトはハッシュで取得
					$row	= mysql_fetch_assoc($result);
//					if( $this->db_encoding && $this->db_encoding != $this->encoding ) {
//						mb_convert_variables($this->encoding,$this->db_encoding,$row);
//					}
					mysql_free_result( $result );
					return $row;
				}
			} else {
				$this->error('該当するデータを一意に絞り込めませんでした:'.$query);
				return false;
			}
		}
	}
	/**
	 * 渡されたSQLクエリ文を実行して1カラム分のみ結果を取得します
	 * @param string $query SQLクエリ
	 * @return 
	 * @access public
	 */
	function _queryOne( $query ) {
		$result	= $this->_query( $query );
		if( false === $result ) {
			$this->error(mysql_error($this->resource_id).':'.$query);
			return false;
		} else {
			if( mysql_num_rows($result) > 0 && mysql_num_fields($result) > 0 ) {
				// 結果が一つ以上あるなら
				$str		= mysql_result( $result, 0, 0 );
//				if( $this->db_encoding != $this->encoding ) {
//					$str	= mb_convert_encoding( $str, $this->encoding, $this->db_encoding );
//				}
				mysql_free_result( $result );
				return $str;
			} else {
//				$this->error('該当するデータが存在しませんでした:'.$query);
//				mysql_free_result( $result );
//				return false;
				return '';
			}
		}
	}
	/**
	 * テーブル名称を指定して該当するテーブル情報ハッシュを取得します。
	 * 【テーブル情報ハッシュの構造】
	 * 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 ) {
		if( strlen(trim($tableName)) == 0 ) {
			return false;
		}
		$sql	= 'SHOW FIELDS FROM ' . $tableName;
		$result	= $this->_queryAll( $sql, null, 'hash' );
		if ( $result === false ) {
			$this->error('データベーステーブル情報の取得に失敗しました。:'.mysql_error($this->resource_id).':'.$sql);
			return false;
		} else {
			$this->debug( '[EXECUTED SELECT] ' . $sql );
			// フィールド一覧を取得できたら
			$fieldHash	= array();
			foreach( $result as $row ) {
				$fieldName	= $row['Field'];
				$type		= substr($row['Type'],0,strpos($row['Type'],'('));
				$size		= substr($row['Type'],strpos($row['Type'],'('),strpos($row['Type'],')'));
				$notNull	= false;
				if( preg_match('/[yY][eE][sS]/',$row['Null']) > 0 ) {
					$notNull	= true;
				}
				$key		= '';
				if( preg_match('/[pP][rR][iI]/',$row['Key']) > 0 ) {
					$key	= 'primary';
				} else if( preg_match('/[uU][nN][iI]/',$row['Key']) > 0 ) {
					$key	= 'unique';
				}
				$isSerial	= false;
				if( preg_match('/[aA][uU][tT][oO]\\_[iI][nN][cC][rR][eE][mM][eE][nN][tT]/',$row['Extra']) > 0 ) {
					$isSerial	= true;
				}
				$hasDefault		= false;
				if(strlen(trim($row['Default'])) > 0 && $row['Default'] != NULL ) {
					$hasDefault	= true;
				}
				$fieldHash[$fieldName]	= array(
					'type'			=> $type,
					'size'			=> $size,
					'not_null'		=> $notNull,
					'key'			=> $key,
					'is_serial'	=> $isSerial,
					'has_default'	=> $hasDefault
				);
			}
			return $fieldHash;
		}
	}
	/**
	 * テーブル存在確認メソッド
	 */
	function _existsTable( $tableName ) {
		$sql	= 'SHOW TABLES';
		$result	= $this->_queryALL( $sql, null, 'hash' );
		if( false === $result || $result == 0 ) {
			$this->error('データベーステーブル情報の取得に失敗しました。:'.mysql_error($this->resource_id).':'.$sql);
			return false;
		} else {
			$this->debug( '[EXECUTED SELECT] ' . $sql );
			foreach( $result as $row ) {
				foreach( $row as $col ) {
					if( $col == $tableName ) {
						return true;
					}
				}
			}
			return false;
		}
	}
	/**
	 * データベース存在確認メソッド
	 */
	function _existsDatabase( $databaseName ) {
		$sql	= 'SHOW DATABASES';
		$result	= $this->_queryALL( $sql, null, 'hash' );
		if( false === $result || $result == 0 ) {
			$this->error('データベース情報の取得に失敗しました。:'.mysql_error($this->resource_id).':'.$sql);
			return false;
		} else {
			$this->debug( '[EXECUTED SELECT] ' . $sql );
			foreach( $result as $row ) {
				foreach( $row as $col ) {
					if( $col == $databaseName ) {
						return true;
					}
				}
			}
			return false;
		}
	}
	/**
	 * 指定データベースのエンコード設定を取得します。
	 */
	function _selectDatabaseEncoding($databaseName) {
		$sql	= 'show create database '.$databaseName;
		$result	= $this->_queryRow( $sql );
		if( false === $result || $result == 0 ) {
			$this->error('データベース文字コードの確認に失敗しました。:'.mysql_error($this->resource_id).':'.$sql);
			return false;
		} else {
			$str	= $result['Create Database'];
			$str	= preg_replace('/(.)+\\s[cC][hH][aA][rR][aA][cC][tT][eE][rR]\\s[sS][eE][tT]\\s/','',$str);
			$str	= trim(preg_replace('/\\s\\*\\//','',$str));
			$this->debug( '[EXECUTED SELECT] ' . $sql );
			if( isset($GLOBALS['DEF_DATABASE2_CONNECTION_MYSQL_ENCODINGS'][$str]) ) {
				return $GLOBALS['DEF_DATABASE2_CONNECTION_MYSQL_ENCODINGS'][$str];
			}
			return $str;
		}
	}
}
?>