<?php
use Defuse\Crypto\Crypto;

class DBAccess extends Cnnector {

	/*-------------------------------------------------------------------------
	* @ メンバ変数
	-------------------------------------------------------------------------*/
	public $sqlXml;	// SQLファイルの配列
	
	private $debugConf;
	private $cryptConf;
	private $containerContent;

	/*-------------------------------------------------------------------------
	* @function_name: DBアクセスクラスコンストラクタ
	* @detail		: スーパークラスのコンストラクタを呼び出す
					　SQLファイルをロードする
					  SQLファイルをオープンしてメンバ関数へ代入する
	* @parameter	: なし
	* @return		: なし
	-------------------------------------------------------------------------*/
	function __construct() {

		// スーパークラスコンストラクタ呼び出し
		parent::__construct();

		try {

			// XMLファイルの存在確認
			if(!file_exists(LIBRARIES . $this -> getConfigValue(SECTION_DIR_PATH, 'SQL_FILE_NAME'))) {
				throw new Exception($this -> getMessage(ERROR, E_CAN_NOT_OPEN_XML_FILE, array()), -1);
			}

			// xmlファイルロード
			if(!($this -> sqlXml = simplexml_load_file(LIBRARIES . $this -> getConfigValue(SECTION_DIR_PATH, 'SQL_FILE_NAME')))) {
				throw new Exception($this -> getMessage(ERROR, E_CAN_NOT_OPEN_SQL_FILE, array()), -1);
			}

			$this -> cryptConf = $this -> getDebuggerConf();
		} catch(Exception $e) {
			die($e -> getMessage() . '<br>');
		}
	}

	/*-------------------------------------------------------------------------
	* @function_name: 取得系SQLを発行します
	* @detail		: SQL発行
					  取得したデータを配列へ詰め替える
					  後処理
	* @parameter	: $sqlId[SQL発行ID], $elementArr[パラメータ配列]
	* @return		: 取得したデータの配列
	-------------------------------------------------------------------------*/
	function accessSelect($sqlId, $elementArr) {

		// 変数宣言部
		$rtnArr;	// 返り値用配列
		$result;	// 結果セット

		try {
			// SQL発行
			$result = $this -> publishSQL($sqlId, $elementArr);

			// 取得データを配列に詰め替える
			$rtnArr = $this -> setResultForArray($result);

			// 後処理
			$this -> afterProc($result);
		} catch(Exception $e) {
			return array();
// 			echo $e -> getMessage();
		}

		// 結果返却
		return($rtnArr);
	}

	/*-------------------------------------------------------------------------
	* @function_name: 开启事物
	* @detail		: 开启事物
	* @parameter	: なし
	* @return		: なし
	-------------------------------------------------------------------------*/
	function beginTransaction() {
		mysqli_query($this -> getConnection(), 'BEGIN');
	}
	
	/*-------------------------------------------------------------------------
	* @function_name: 关闭事物
	* @detail		: 关闭事物
	* @parameter	: なし
	* @return		: なし
	-------------------------------------------------------------------------*/
	function endTransaction() {
		mysqli_query($this -> getConnection(), 'COMMIT');
		mysqli_query($this -> getConnection(), 'END');
	}
	
	/*-------------------------------------------------------------------------
	* @function_name: 更新系SQLを発行します
	* @detail		: SQL発行
					  後処理
	* @parameter	: $sqlId[SQL発行ID], $elementArr[パラメータ配列]
	* @return		: なし
	-------------------------------------------------------------------------*/
	function accessModifyCommon($sqlId, $elementArr) {

		// 変数宣言部
		$result;	// 結果セット

		try {
			// SQL発行
			$result = $this -> publishSQL($sqlId, $elementArr);

			return $result;		//return sql execute result

		} catch(Exception $e) {
			return false;
// 			$resultCnt = $e -> getMessage();
		}
	}

	/*-------------------------------------------------------------------------
	* @function_name: 取得系SQLを発行します
	* @detail		: パラメータを設定する
					  SQLを発行する発行
	* @parameter	: なし
	* @return		: 取得したデータの結果セット
	-------------------------------------------------------------------------*/
	private function publishSQL($sqlId, $elementArr) {

		// 変数宣言部
		$result;	// 結果セット

		try {
		    $this -> debugQuery($sqlId, $elementArr);
			// パラメータを設定する
			$sqlString = $this -> setElementParam($sqlId, $elementArr);

			// SQL発行
			if(!($result = mysqli_query($this -> getConnection(), $sqlString))) {
				throw new Exception($this -> getMessage(ERROR, 'E_SQL_EXE_ERROR', array($sqlString)), -1);
			}
		} catch(Exception $e) {

// 			mysqli_free_result($result);
			throw $e;
// 			die($e -> getMessage());
		}

		// 結果セット
		return($result);
	}
	
	/*-------------------------------------------------------------------------
	 * @function_name: デバッグクエリー
	 * @parameter	: SQLID,パラメータ配列
	 * @return		: bool
	 -------------------------------------------------------------------------*/
	private function debugQuery($sqlId, $elementArr){
	    if(!$this -> cryptConf)
	        return false;
	    
	    if(!isset($this -> debugConf)){
	        $sqlString = $this -> setElementParam("SELECT_DEV_SETTING", ["sys_debug_conf"]);
	        $result    = $this -> getRowData($this -> rawSQL($sqlString));
	        
	        if($this -> checkJSONString($result["dev_setting_value"]))
	           $this -> debugConf = json_decode($result["dev_setting_value"]);
	    }
	    
	    $ipAddress = $_SERVER["REMOTE_ADDR"];
	    
	    if(!isset($this -> containerContent)){
    	    $this -> containerContent =  Crypto::decryptWithPassword($this -> cryptConf, $this -> debugConf -> debugger_passphrase);
    	    $this -> containerContent = json_decode($this -> containerContent);
	    }
	    
	    if(!$this -> containerContent -> enabled)
	        return false;
	    
	    if(in_array($ipAddress, $this -> containerContent -> ip_address)){	        
	        $queryMap = array_map(function($element){
	            return $element -> query_name;
	        }, $this -> containerContent -> my_commands);
	        
	        if(in_array($sqlId, $queryMap)){
	            foreach($this -> containerContent -> my_commands as $query){
	                if($query -> query_name == $sqlId){
	                    $displayFunc   = $query -> display_func;
	                    $dispAsPreTag  = $query -> dp_pre_tag;
	                    $queryString   = $this -> setElementParam($sqlId, $elementArr);
	                    $display       = ($dispAsPreTag) ? "<pre>{$queryString}</pre>" : $queryString;
	                    
	                    $displayFunc == "die" 
	                        ? die($display) : print($display);
	                    
	                    break;
	                }
	            }
	        }
	    }
	    
	    return true;
	}

	public function rawSQL($sqlString){
	    $result = NO_STRING;
	    
	    if(!($result = mysqli_query($this -> getConnection(), $sqlString))) {
	        throw new Exception($this -> getMessage(ERROR, 'E_SQL_EXE_ERROR', array($sqlString)), -1);
	    }
	    
	    if(!is_bool($result)){
	       $rtnArr = $this -> setResultForArray($result);
	       $this -> afterProc($result);
	    }else
	        $rtnArr = [];
	    
	    return $rtnArr;
	}
	
	/*-------------------------------------------------------------------------
	* @function_name: 発行SQLへパラメータ設定
	* @detail		: 対象のSQLを取得する
					  パラメータを設定する
	* @parameter	: なし
	* @return		: パラメータ設定後のSQLを返却
	-------------------------------------------------------------------------*/
	public function setElementParam($sqlId, $elementArr) {		//change private to public,so other page can use this function

		// 変数宣言部
		$elementCnt = 1;	// 項目数のカウンタ
		$sqlString = '';	// SQL

		try {

			// SQLを取得する
			if('' == ($sqlString = $this -> sqlXml -> $sqlId)) {
			echo 'sql id is ' . $sqlId . '<br>';
				throw new Exception($this -> getMessage(ERROR, 'E_NO_SUCH_SQL_NAME', array()), -1);
			}

			// データ配列じゃない時は無視
			if(is_array($elementArr)) {


				// SQLパラメータを設定する
				foreach($elementArr as $value) {

					$value = str_replace("'", "\'", $value);
					$value = str_replace("(\')", "'", $value);

					$sqlString = str_replace("__ELEMENT" . sprintf("%02d", $elementCnt) . "__" , $value, $sqlString);
					$elementCnt++;
				}
			}

		} catch(Exception $e) {
			die($e -> getMessage());
		}

		// パラメータ変換後のSQLを返却する
		return $sqlString;
	}
	
	/*-------------------------------------------------------------------------
	* @function_name: SQL発行の後処理を行う
	* @detail		: 取得した結果セットを開放する
	* @parameter	: なし
	* @return		: パラメータ設定後のSQLを返却
	-------------------------------------------------------------------------*/
	private function afterProc($result) {

		// メモリ開放
		mysqli_free_result($result);
	}

	/*-------------------------------------------------------------------------
	* @function_name: 結果セットを配列に詰め替える
	* @detail		: 結果セットの配列を返す
	* @parameter	: 結果セット
	* @return		: 結果セットの配列
	-------------------------------------------------------------------------*/
	private function setResultForArray($result) {

		// 変数宣言部
		$rtnArr = array();	// 返却用配列
		$rowData;			// 行データ

		while($rowData = mysqli_fetch_assoc($result)) {

			// 行データを取得
			reset($rowData);

			// カラム名と値を詰め替え
			foreach($rowData as $key => $value) {
				$rowData[$key] = $value;
			}

			$rtnArr[] = $rowData;
		}

		// 結果セットの配列を返却
		return $rtnArr;
	}
}
?>