'Database connect failed', BX_UPGRADE_DB_ERR_QUERY_ERROR => 'Database query error', BX_UPGRADE_DB_ERR_ESCAPE => 'Escape string error' ); protected $_bPdoPersistent; protected $_iPdoFetchType; protected $_iPdoErrorMode; protected $_bErrorChecking; protected $_aError; protected $_sHost, $_sPort, $_sSocket, $_sDbname, $_sUser, $_sPassword, $_sCharset, $_sStorageEngine; protected $_oStatement = null; protected $_oDbCacheObject = null; /** * set database parameters and connect to it */ protected function __construct() { if (isset($GLOBALS['bxDolClasses'][get_class($this)])) trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR); $this->_bPdoPersistent = true; $this->_iPdoFetchType = PDO::FETCH_ASSOC; $this->_iPdoErrorMode = PDO::ERRMODE_EXCEPTION; $this->_bErrorChecking = true; $this->_aError = array(); $this->_sStorageEngine = 'MYISAM'; $this->_sCharset = 'utf8mb4'; $this->_sHost = is_array(BX_DATABASE_HOST) ? array_values(BX_DATABASE_HOST)[0] : BX_DATABASE_HOST; $this->_sPort = is_array(BX_DATABASE_PORT) ? array_values(BX_DATABASE_PORT)[0] : BX_DATABASE_PORT; $this->_sSocket = is_array(BX_DATABASE_SOCK) ? array_values(BX_DATABASE_SOCK)[0] : BX_DATABASE_SOCK; $this->_sDbname = is_array(BX_DATABASE_NAME) ? array_values(BX_DATABASE_NAME)[0] : BX_DATABASE_NAME; $this->_sUser = is_array(BX_DATABASE_USER) ? array_values(BX_DATABASE_USER)[0] : BX_DATABASE_USER; $this->_sPassword = is_array(BX_DATABASE_PASS) ? array_values(BX_DATABASE_PASS)[0] : BX_DATABASE_PASS; if (defined('BX_DATABASE_ENGINE')) $this->_sStorageEngine = is_array(BX_DATABASE_ENGINE) ? array_values(BX_DATABASE_ENGINE)[0] : BX_DATABASE_ENGINE; @set_exception_handler(array($this, 'pdoExceptionHandler')); } /** * Get singleton instance of the class */ public static function getInstance() { if(!isset($GLOBALS['bxDolClasses'][__CLASS__])) { if(!defined('BX_DATABASE_HOST')) return null; $o = new BxDolUpgradeDb(); $o->connect(); $GLOBALS['bxDolClasses'][__CLASS__] = $o; } return $GLOBALS['bxDolClasses'][__CLASS__]; } public static function getLink() { return self::$_rLink; } /** * connect to database with appointed parameters */ public function connect() { if(self::$_rLink) return; try { $sDsn = "mysql:host=" . $this->_sHost . ";"; $sDsn .= $this->_sPort ? "port=" . $this->_sPort . ";" : ""; $sDsn .= $this->_sSocket ? "unix_socket=" . $this->_sSocket . ";" : ""; $sDsn .= "dbname=" . $this->_sDbname . ";charset=" . $this->_sCharset; self::$_rLink = new PDO($sDsn, $this->_sUser, $this->_sPassword, array( PDO::ATTR_ERRMODE => $this->_iPdoErrorMode, PDO::ATTR_DEFAULT_FETCH_MODE => $this->_iPdoFetchType, PDO::ATTR_PERSISTENT => $this->_bPdoPersistent )); $this->pdoExec("SET NAMES 'utf8mb4' COLLATE '" . (defined('BX_DATABASE_COLLATE') ? BX_DATABASE_COLLATE : 'utf8mb4_unicode_ci') . "'"); $this->pdoExec("SET sql_mode = ''"); $sVer = $this->getVersion(); $sStorageEngine = !$sVer || version_compare($sVer, '5.7.5', '>=') ? 'default_storage_engine' : 'storage_engine'; $this->pdoExec("SET $sStorageEngine=" . $this->_sStorageEngine); self::$_aDbCacheData = array(); } catch (PDOException $oException) { $oException->errorInfo[self::$_sErrorKey] = array( 'code' => BX_UPGRADE_DB_ERR_CONNECT_FAILD, 'message' => $oException->getMessage(), 'trace' => $oException->getTrace() ); throw $oException; } } /** * close mysql connection */ public function disconnect() { self::$_rLink = null; } /** * check mysql connection */ public function ping() { try { $this->pdoQuery("SELECT 1"); } catch (PDOException $e) { return false; } return true; } /** * Can be used to execute queries which shouldn't return data */ public function pdoExec($sQuery) { return self::$_rLink->exec($sQuery); } /** * Executes query and returns PDOStatement object or false */ public function pdoQuery($sQuery) { return self::$_rLink->query($sQuery); } /** * database query exception handler for exceptions appeared out of the try/catch block */ public function pdoExceptionHandler($oException) { if(!($oException instanceof PDOException)) return; $this->error($oException->errorInfo[self::$_sErrorKey]); return; } /** * get mysql option */ function getOption($sName) { $oStatement = $this->pdoQuery("SELECT @@{$sName}"); return $this->getOne($oStatement); } /** * execute sql query and return one value result */ public function getOne($oStatement, $aBindings = array(), $iIndex = 0) { if(!$oStatement) return false; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); $aResult = array(); if($this->res($oStatement, $aBindings)) $aResult = $oStatement->fetch(PDO::FETCH_NUM); return is_array($aResult) && count($aResult) ? $aResult[$iIndex] : false; } /** * execute sql query and return one row result */ function getRow($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC) { $aResult = array(); if(!$oStatement) return $aResult; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH))) $iFetchType = $this->_iPdoFetchType; if(!$this->res($oStatement, $aBindings)) return array(); $aResult = $oStatement->fetch($iFetchType); if($aResult === false) return array(); return $aResult; } /** * execute sql query and return a column as result */ function getColumn($oStatement, $aBindings = array(), $iFetchColumnNumber = 0) { $aResult = array(); if(!$oStatement) return $aResult; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); if(!$this->res($oStatement, $aBindings)) return array(); $aResult = $oStatement->fetchAll(PDO::FETCH_COLUMN, $iFetchColumnNumber); if($aResult === false) return array(); return $aResult; } /** * execute sql query and return the first row of result * and keep $array type and poiter to all data */ public function getFirstRow($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC) { if(!$oStatement) return array(); else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH))) $iFetchType = $this->_iPdoFetchType; if(!$this->res($oStatement, $aBindings)) return array(); $aResult = $oStatement->fetch($iFetchType); if($aResult === false) return array(); $this->_oStatement = $oStatement; return $aResult; } /** * return next row of pointed last getFirstRow calling data */ public function getNextRow($iFetchType = PDO::FETCH_ASSOC) { if(!$this->_oStatement) return array(); if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH))) $iFetchType = $this->_iPdoFetchType; $aResult = $this->_oStatement->fetch($iFetchType); if($aResult !== false) return $aResult; $this->_oStatement = null; return array(); } /** * execute sql query and return table of records as result */ public function getAll($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC) { $aResult = array(); if(!$oStatement) return $aResult; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH))) $iFetchType = $this->_iPdoFetchType; if(!$this->res($oStatement, $aBindings)) return array(); $aResult = $oStatement->fetchAll($iFetchType); if($aResult === false) return array(); return $aResult; } /** * Executes sql query and returns table of records as result. * * @deprecated use getAll instead. */ public function fillArray($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC) { return $this->getAll($oStatement, $aBindings); } /** * execute sql query and return table of records as result */ public function getAllWithKey($oStatement, $sFieldKey, $aBindings = array()) { $aResult = array(); if(!$oStatement) return $aResult; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); $aRow = $this->getFirstRow($oStatement, $aBindings, PDO::FETCH_ASSOC); while(!empty($aRow)) { $aResult[$aRow[$sFieldKey]] = $aRow; $aRow = $this->getNextRow(PDO::FETCH_ASSOC); } return $aResult; } /** * execute sql query and return table of records as result */ public function getPairs($oStatement, $sFieldKey, $sFieldValue, $aBindings = array()) { $aResult = array(); if(!$oStatement) return $aResult; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); $aRow = $this->getFirstRow($oStatement, $aBindings, PDO::FETCH_ASSOC); while(!empty($aRow)) { $aResult[$aRow[$sFieldKey]] = $aRow[$sFieldValue]; $aRow = $this->getNextRow(PDO::FETCH_ASSOC); } return $aResult; } /** * return number of affected rows in current mysql result * * NOTE: PDOStatement::rowCount works for SELECT queries in MySQL. * So, this method should be rewritten if the other DB engine will be used. */ public function getNumRows($oStatement = null) { if($oStatement && ($oStatement instanceof PDOStatement)) return $oStatement->rowCount(); if($this->_oStatement && ($this->_oStatement instanceof PDOStatement)) return $this->_oStatement->rowCount(); return 0; } /** * returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement. */ public function getAffectedRows($oStatement = null) { return $this->getNumRows($oStatement); } public function lastId() { return self::$_rLink->lastInsertId(); } /** * execute any query return number of rows affected/false */ public function query($oStatement, $aBindings = array(), $bVerbose = null) { if(!$oStatement) return false; else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) $oStatement = $this->prepare($oStatement); if($this->res($oStatement, $aBindings, $bVerbose)) return $oStatement->rowCount(); return false; } /** * execute any query */ public function res($oStatement, $aBindings = array(), $bVerbose = null) { if(!$oStatement || !($oStatement instanceof PDOStatement)) return false; if($oStatement->errorCode() == BX_UPGRADE_PDO_STATE_SUCCESS) return true; if(isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginQuery($oStatement->queryString); $bResult = $this->executeStatement($oStatement, $aBindings, $bVerbose); //if mysql connection is lost - reconnect and try again if(!$bResult && !$this->ping()) { $this->disconnect(); $this->connect(); $bResult = $this->executeStatement($oStatement, $aBindings, $bVerbose); } if(isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endQuery($bResult); //is needed for SILENT mode if(!$bResult && !empty($this->_aError)) $this->error($this->_aError); return $bResult; } /** * get mysql server info */ public function getServerInfo() { return self::$_rLink->getAttribute(PDO::ATTR_SERVER_VERSION); } /** * get mysql version */ public function getVersion() { $s = $this->getOne("SELECT VERSION()"); return preg_match("/([0-9\.]+)/", $s, $m) ? $m[1] : false; } /** * get list of tables in database */ public function listTables() { $oStatement = $this->pdoQuery("SHOW TABLES FROM `" . BX_DATABASE_NAME . "`"); return $this->getColumn($oStatement); } public function getFields($sTable) { $oStatement = $this->pdoQuery("SHOW COLUMNS FROM `" . $sTable . "`"); $aFields = $this->getAll($oStatement); $aResult = array('original' => array(), 'uppercase' => array()); foreach($aFields as $aField) { $aResult['original'][] = $aField['Field']; $aResult['uppercase'][] = strtoupper($aField['Field']); } return $aResult; } public function isFieldExists($sTable, $sFieldName) { $aFields = $this->getFields($sTable); return in_array(strtoupper($sFieldName), $aFields['uppercase']); } public function isIndexExists($sTable, $sIndexName) { $aIndexes = $this->getAll("SHOW INDEXES FROM `" . $sTable . "`"); foreach ($aIndexes as $aIndex) if ($aIndex['Key_name'] == $sIndexName) return true; return false; } public function error($aError) { $sErrorType = self::$_aErrors[$aError['code']]; $bVerbose = isset($aError['verbose']) ? (bool)$aError['verbose'] : $this->_bErrorChecking; if(!$bVerbose) { $this->log($sErrorType . ': ' . $aError['message']); return; } if(defined('BX_UPGRADE_DB_FULL_VISUAL_PROCESSING') && BX_UPGRADE_DB_FULL_VISUAL_PROCESSING) { $sOutput = '
Query:
' . $aError['query'] . '
Mysql error:
' . $aError['message'] . '
Location:
The error was found in ' . $aErrorLocation['function'] . ' function in the file ' . $aErrorLocation['file'] . ' at line ' . $aErrorLocation['line'] . '.
' . htmlspecialchars_adv($sBackTrace) . '
' . htmlspecialchars_adv($sSettings) . '
Called script:
' . $_SERVER['PHP_SELF'] . '
Request parameters:
' . htmlspecialchars_adv($sRequest) . ''; } return $sOutput; } } /** @} */