BxDolDb.php 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303
  1. <?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. define('BX_DB_MODE_SILENT', PDO::ERRMODE_SILENT);
  10. define('BX_DB_MODE_EXCEPTION', PDO::ERRMODE_EXCEPTION);
  11. define('BX_DB_ERR_CONNECT_FAILD', 1);
  12. define('BX_DB_ERR_QUERY_ERROR', 2);
  13. define('BX_DB_ERR_ESCAPE', 3);
  14. define('BX_PDO_STATE_NOT_EXECUTED', NULL);
  15. define('BX_PDO_STATE_SUCCESS', '00000');
  16. class BxDolDb extends BxDolFactory implements iBxDolSingleton
  17. {
  18. protected static $_rLink;
  19. protected static $_aDbCacheData;
  20. protected static $_aParams;
  21. protected static $_sParamsCacheName = 'sys_options';
  22. protected static $_sParamsCacheNameMixed = 'sys_options_mixed_';
  23. protected static $_sParamsCacheNameMix = 'sys_options_mix_';
  24. protected static $_sErrorKey = 'bx_db_error';
  25. protected static $_aErrors = array(
  26. BX_DB_ERR_CONNECT_FAILD => 'Database connect failed',
  27. BX_DB_ERR_QUERY_ERROR => 'Database query error',
  28. BX_DB_ERR_ESCAPE => 'Escape string error'
  29. );
  30. protected static $_bReadOnlyMode = true;
  31. protected static $_bMultuServersMode = false;
  32. protected $_bPdoPersistent;
  33. protected $_iPdoFetchType;
  34. protected $_iPdoErrorMode;
  35. protected $_bErrorChecking;
  36. protected $_aError;
  37. protected $_sHost, $_sPort, $_sSocket, $_sDbname, $_sUser, $_sPassword, $_sCharset, $_sStorageEngine;
  38. protected $_oStatement = null;
  39. protected $_oDbCacheObject = null;
  40. /**
  41. * set database parameters and connect to it
  42. */
  43. protected function __construct($aDbConf = false)
  44. {
  45. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  46. trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
  47. parent::__construct();
  48. $this->_bPdoPersistent = true;
  49. $this->_iPdoFetchType = PDO::FETCH_ASSOC;
  50. $this->_iPdoErrorMode = BX_DB_MODE_EXCEPTION;
  51. $this->_bErrorChecking = true;
  52. $this->_aError = array();
  53. $this->_sStorageEngine = 'MYISAM';
  54. if (defined('BX_DATABASE_ENGINE'))
  55. $this->_sStorageEngine = BX_DATABASE_ENGINE;
  56. $this->_sCharset = 'utf8mb4';
  57. if($aDbConf === false) {
  58. $this->_sHost = BX_DATABASE_HOST;
  59. $this->_sPort = BX_DATABASE_PORT;
  60. $this->_sSocket = BX_DATABASE_SOCK;
  61. $this->_sDbname = BX_DATABASE_NAME;
  62. $this->_sUser = BX_DATABASE_USER;
  63. $this->_sPassword = BX_DATABASE_PASS;
  64. }
  65. else {
  66. $this->_sHost = $aDbConf['host'];
  67. $this->_sPort = $aDbConf['port'];
  68. $this->_sSocket = $aDbConf['sock'];
  69. $this->_sDbname = $aDbConf['name'];
  70. $this->_sUser = $aDbConf['user'];
  71. $this->_sPassword = $aDbConf['pwd'];
  72. if(isset($aDbConf['charset']))
  73. $this->_sCharset = $aDbConf['charset'];
  74. if(isset($aDbConf['error_checking']))
  75. $this->_bErrorChecking = $aDbConf['error_checking'];
  76. if(isset($aDbConf['storage_engine']))
  77. $this->_sStorageEngine = $aDbConf['storage_engine'];
  78. }
  79. @set_exception_handler(array($this, 'pdoExceptionHandler'));
  80. }
  81. /**
  82. * Prevent cloning the instance
  83. */
  84. public function __clone()
  85. {
  86. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  87. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  88. }
  89. /**
  90. * Get singleton instance of the class
  91. */
  92. public static function getInstance()
  93. {
  94. $null = null;
  95. return self::getInstanceWithConf(false, $null);
  96. }
  97. public static function getInstanceWithConf($aDbConf, &$sError)
  98. {
  99. if(!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
  100. if($aDbConf === false && !defined('BX_DATABASE_HOST'))
  101. return null;
  102. $o = new BxDolDb($aDbConf);
  103. $sErrorMessage = $o->connect();
  104. if($sErrorMessage) {
  105. if($sError !== null)
  106. $sError = $sErrorMessage;
  107. return null;
  108. }
  109. $GLOBALS['bxDolClasses'][__CLASS__] = $o;
  110. }
  111. return $GLOBALS['bxDolClasses'][__CLASS__];
  112. }
  113. public static function getLink()
  114. {
  115. return self::$_rLink;
  116. }
  117. public function setReadOnlyMode($b)
  118. {
  119. if ($b == self::$_bReadOnlyMode)
  120. return;
  121. self::$_bReadOnlyMode = $b;
  122. if (self::$_bMultuServersMode) {
  123. $this->disconnect();
  124. $this->connect();
  125. }
  126. }
  127. protected function balancer()
  128. {
  129. // regular mode - one DB server
  130. if (!is_array($this->_sHost))
  131. return array($this->_sHost, $this->_sPort, $this->_sSocket, $this->_sUser, $this->_sPassword, $this->_sDbname);
  132. $iServersNum = count($this->_sHost);
  133. if ($iServersNum > 1)
  134. self::$_bMultuServersMode = true;
  135. // in read/write mode always use first server, which should be always master (also check for regular mode but with array with 1 item)
  136. if (!self::$_bReadOnlyMode || 1 == $iServersNum)
  137. $i = 0;
  138. // in case of 2 servers - always use second server, in read only mode
  139. elseif (2 == $iServersNum)
  140. $i = 1;
  141. // when there are more that 2 servers, randomly select read only server
  142. else
  143. $i = rand(1, $iServersNum-1);
  144. return array($this->_sHost[$i], $this->_sPort[$i], $this->_sSocket[$i], $this->_sUser[$i], $this->_sPassword[$i], $this->_sDbname[$i]);
  145. }
  146. /**
  147. * connect to database with appointed parameters
  148. */
  149. public function connect()
  150. {
  151. if(self::$_rLink)
  152. return;
  153. list ($sHost, $sPort, $sSocket, $sUser, $sPassword, $sDBName) = $this->balancer();
  154. try {
  155. $sDsn = "mysql:host=" . $sHost . ";";
  156. $sDsn .= $sPort ? "port=" . $sPort . ";" : "";
  157. $sDsn .= $sSocket ? "unix_socket=" . $sSocket . ";" : "";
  158. $sDsn .= "dbname=" . $sDBName . ";charset=" . $this->_sCharset;
  159. self::$_rLink = new PDO($sDsn, $sUser, $sPassword, array(
  160. PDO::ATTR_ERRMODE => $this->_iPdoErrorMode,
  161. PDO::ATTR_DEFAULT_FETCH_MODE => $this->_iPdoFetchType,
  162. PDO::ATTR_PERSISTENT => $this->_bPdoPersistent
  163. ));
  164. $this->pdoExec("SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'");
  165. $this->pdoExec("SET sql_mode = ''");
  166. if (0 == strcasecmp($this->_sStorageEngine, 'INNODB'))
  167. $this->pdoExec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
  168. $sVer = $this->getVersion();
  169. $sStorageEngine = !$sVer || version_compare($sVer, '5.7.5', '>=') ? 'default_storage_engine' : 'storage_engine';
  170. $this->pdoExec("SET $sStorageEngine=" . $this->_sStorageEngine);
  171. $this->cleanMemoryAll();
  172. }
  173. catch (PDOException $oException) {
  174. $oException->errorInfo[self::$_sErrorKey] = array(
  175. 'code' => BX_DB_ERR_CONNECT_FAILD,
  176. 'message' => $oException->getMessage(),
  177. 'trace' => $oException->getTrace()
  178. );
  179. throw $oException;
  180. }
  181. }
  182. /**
  183. * close mysql connection
  184. */
  185. public function disconnect()
  186. {
  187. self::$_rLink = null;
  188. }
  189. /**
  190. * check mysql connection
  191. */
  192. public function ping()
  193. {
  194. try {
  195. $this->pdoQuery("SELECT 1");
  196. }
  197. catch (PDOException $e) {
  198. return false;
  199. }
  200. return true;
  201. }
  202. /**
  203. * Can be used to execute queries which shouldn't return data
  204. */
  205. public function pdoExec($sQuery)
  206. {
  207. return self::$_rLink->exec($sQuery);
  208. }
  209. /**
  210. * Executes query and returns PDOStatement object or false
  211. */
  212. public function pdoQuery($sQuery)
  213. {
  214. return self::$_rLink->query($sQuery);
  215. }
  216. /**
  217. * database query exception handler for exceptions appeared out of the try/catch block
  218. */
  219. public function pdoExceptionHandler($oException)
  220. {
  221. if(!($oException instanceof PDOException)) {
  222. throw $oException;
  223. return;
  224. }
  225. if(!isset($oException->errorInfo[self::$_sErrorKey]))
  226. $oException->errorInfo[self::$_sErrorKey] = array(
  227. 'code' => BX_DB_ERR_QUERY_ERROR,
  228. 'message' => !empty($oException->errorInfo[2]) ? $oException->errorInfo[2] : $oException->getMessage(),
  229. 'trace' => $oException->getTrace()
  230. );
  231. $this->error($oException->errorInfo[self::$_sErrorKey]);
  232. }
  233. /**
  234. * get mysql option
  235. */
  236. function getOption($sName)
  237. {
  238. $oStatement = $this->pdoQuery("SELECT @@{$sName}");
  239. return $this->getOne($oStatement);
  240. }
  241. /**
  242. * execute sql query and return one value result
  243. */
  244. public function getOne($oStatement, $aBindings = array(), $iIndex = 0)
  245. {
  246. if(!$oStatement)
  247. return false;
  248. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  249. $oStatement = $this->prepare($oStatement);
  250. $aResult = array();
  251. if($this->res($oStatement, $aBindings))
  252. $aResult = $oStatement->fetch(PDO::FETCH_NUM);
  253. return is_array($aResult) && count($aResult) ? $aResult[$iIndex] : false;
  254. }
  255. /**
  256. * execute sql query and return one row result
  257. */
  258. function getRow($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC)
  259. {
  260. $aResult = array();
  261. if(!$oStatement)
  262. return $aResult;
  263. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  264. $oStatement = $this->prepare($oStatement);
  265. if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH)))
  266. $iFetchType = $this->_iPdoFetchType;
  267. if(!$this->res($oStatement, $aBindings))
  268. return array();
  269. $aResult = $oStatement->fetch($iFetchType);
  270. if($aResult === false)
  271. return array();
  272. return $aResult;
  273. }
  274. /**
  275. * execute sql query and return a column as result
  276. */
  277. function getColumn($oStatement, $aBindings = array(), $iFetchColumnNumber = 0)
  278. {
  279. $aResult = array();
  280. if(!$oStatement)
  281. return $aResult;
  282. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  283. $oStatement = $this->prepare($oStatement);
  284. if(!$this->res($oStatement, $aBindings))
  285. return array();
  286. $aResult = $oStatement->fetchAll(PDO::FETCH_COLUMN, $iFetchColumnNumber);
  287. if($aResult === false)
  288. return array();
  289. return $aResult;
  290. }
  291. /**
  292. * execute sql query and return the first row of result
  293. * and keep $array type and poiter to all data
  294. */
  295. public function getFirstRow($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC)
  296. {
  297. if(!$oStatement)
  298. return array();
  299. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  300. $oStatement = $this->prepare($oStatement);
  301. if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH)))
  302. $iFetchType = $this->_iPdoFetchType;
  303. if(!$this->res($oStatement, $aBindings))
  304. return array();
  305. $aResult = $oStatement->fetch($iFetchType);
  306. if($aResult === false)
  307. return array();
  308. $this->_oStatement = $oStatement;
  309. return $aResult;
  310. }
  311. /**
  312. * return next row of pointed last getFirstRow calling data
  313. */
  314. public function getNextRow($iFetchType = PDO::FETCH_ASSOC)
  315. {
  316. if(!$this->_oStatement)
  317. return array();
  318. if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH)))
  319. $iFetchType = $this->_iPdoFetchType;
  320. $aResult = $this->_oStatement->fetch($iFetchType);
  321. if($aResult !== false)
  322. return $aResult;
  323. $this->_oStatement = null;
  324. return array();
  325. }
  326. /**
  327. * execute sql query and return table of records as result
  328. */
  329. public function getAll($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC)
  330. {
  331. $aResult = array();
  332. if(!$oStatement)
  333. return $aResult;
  334. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  335. $oStatement = $this->prepare($oStatement);
  336. if(!in_array($iFetchType, array(PDO::FETCH_NUM, PDO::FETCH_ASSOC, PDO::FETCH_BOTH)))
  337. $iFetchType = $this->_iPdoFetchType;
  338. if(!$this->res($oStatement, $aBindings))
  339. return array();
  340. $aResult = $oStatement->fetchAll($iFetchType);
  341. if($aResult === false)
  342. return array();
  343. return $aResult;
  344. }
  345. /**
  346. * Executes sql query and returns table of records as result.
  347. *
  348. * @deprecated use getAll instead.
  349. */
  350. public function fillArray($oStatement, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC)
  351. {
  352. return $this->getAll($oStatement, $aBindings, $iFetchType);
  353. }
  354. /**
  355. * execute sql query and return table of records as result
  356. */
  357. public function getAllWithKey($oStatement, $sFieldKey, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC)
  358. {
  359. $aResult = array();
  360. if(!$oStatement)
  361. return $aResult;
  362. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  363. $oStatement = $this->prepare($oStatement);
  364. $aRow = $this->getFirstRow($oStatement, $aBindings, $iFetchType);
  365. while(!empty($aRow)) {
  366. $aResult[$aRow[$sFieldKey]] = $aRow;
  367. $aRow = $this->getNextRow($iFetchType);
  368. }
  369. return $aResult;
  370. }
  371. /**
  372. * execute sql query and return table of records as result
  373. */
  374. public function getPairs($oStatement, $sFieldKey, $sFieldValue, $aBindings = array(), $iFetchType = PDO::FETCH_ASSOC)
  375. {
  376. $aResult = array();
  377. if(!$oStatement)
  378. return $aResult;
  379. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  380. $oStatement = $this->prepare($oStatement);
  381. $aRow = $this->getFirstRow($oStatement, $aBindings, $iFetchType);
  382. while(!empty($aRow)) {
  383. $aResult[$aRow[$sFieldKey]] = $aRow[$sFieldValue];
  384. $aRow = $this->getNextRow($iFetchType);
  385. }
  386. return $aResult;
  387. }
  388. /**
  389. * return number of affected rows in current mysql result
  390. *
  391. * NOTE: PDOStatement::rowCount works for SELECT queries in MySQL.
  392. * So, this method should be rewritten if the other DB engine will be used.
  393. */
  394. public function getNumRows($oStatement = null)
  395. {
  396. if($oStatement && ($oStatement instanceof PDOStatement))
  397. return $oStatement->rowCount();
  398. if($this->_oStatement && ($this->_oStatement instanceof PDOStatement))
  399. return $this->_oStatement->rowCount();
  400. return 0;
  401. }
  402. /**
  403. * returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement.
  404. */
  405. public function getAffectedRows($oStatement = null)
  406. {
  407. return $this->getNumRows($oStatement);
  408. }
  409. public function lastId()
  410. {
  411. return self::$_rLink->lastInsertId();
  412. }
  413. /**
  414. * execute any query return number of rows affected/false
  415. */
  416. public function query($oStatement, $aBindings = array(), $bVerbose = null)
  417. {
  418. if(!$oStatement)
  419. return false;
  420. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement))
  421. $oStatement = $this->prepare($oStatement);
  422. if($this->res($oStatement, $aBindings, $bVerbose))
  423. return $oStatement->rowCount();
  424. return false;
  425. }
  426. /**
  427. * execute any query
  428. */
  429. public function res($oStatement, $aBindings = array(), $bVerbose = null)
  430. {
  431. if(!$oStatement || !($oStatement instanceof PDOStatement))
  432. return false;
  433. if($oStatement->errorCode() == BX_PDO_STATE_SUCCESS)
  434. return true;
  435. if(isset($GLOBALS['bx_profiler']))
  436. $GLOBALS['bx_profiler']->beginQuery($oStatement->queryString);
  437. for ($iAttempts = 3; $iAttempts > 0; --$iAttempts) {
  438. $oException = null;
  439. $iErrorCode = 0;
  440. $bResult = false;
  441. try {
  442. $bResult = $this->executeStatement($oStatement, $aBindings, $bVerbose, $iErrorCode);
  443. } catch (PDOException $oException) {
  444. $iErrorCode = $oException->errorInfo[1];
  445. }
  446. // if deadlock timeout - try again, make sure that deadlock timeout is short
  447. if (!$bResult && (1205 == $iErrorCode || 1213 == $iErrorCode)) {
  448. continue;
  449. }
  450. // if mysql connection is lost - reconnect and try again
  451. if(!$bResult && !$this->ping()) {
  452. $this->disconnect();
  453. $this->connect();
  454. continue;
  455. }
  456. // in all other cases exit from the loop
  457. if ($oException)
  458. throw $oException;
  459. break;
  460. }
  461. if(isset($GLOBALS['bx_profiler']))
  462. $GLOBALS['bx_profiler']->endQuery($oStatement);
  463. //is needed for SILENT mode
  464. if(!$bResult && !empty($this->_aError))
  465. $this->error($this->_aError);
  466. return $bResult;
  467. }
  468. /**
  469. * get mysql server info
  470. */
  471. public function getServerInfo()
  472. {
  473. return self::$_rLink->getAttribute(PDO::ATTR_SERVER_VERSION);
  474. }
  475. /**
  476. * get mysql version
  477. */
  478. public function getVersion()
  479. {
  480. $s = $this->getOne("SELECT VERSION()");
  481. return preg_match("/([0-9\.]+)/", $s, $m) ? $m[1] : false;
  482. }
  483. /**
  484. * get list of tables in database
  485. */
  486. public function listTables()
  487. {
  488. $oStatement = $this->pdoQuery("SHOW TABLES FROM `" . BX_DATABASE_NAME . "`");
  489. return $this->getColumn($oStatement);
  490. }
  491. public function getFields($sTable)
  492. {
  493. $oStatement = $this->pdoQuery("SHOW COLUMNS FROM `" . $sTable . "`");
  494. $aFields = $this->getAll($oStatement);
  495. $aResult = array('original' => array(), 'uppercase' => array());
  496. foreach($aFields as $aField) {
  497. $aResult['original'][] = $aField['Field'];
  498. $aResult['uppercase'][] = strtoupper($aField['Field']);
  499. }
  500. return $aResult;
  501. }
  502. public function fetchField($oStatement, $iField, $aBindings = array())
  503. {
  504. $aResult = array();
  505. if(!$oStatement)
  506. return $aResult;
  507. else if(!($oStatement instanceof PDOStatement) && is_string($oStatement)) {
  508. $oStatement = $this->prepare($oStatement);
  509. if(!$this->res($oStatement, $aBindings))
  510. return $aResult;
  511. }
  512. return $oStatement->getColumnMeta($iField);
  513. }
  514. public function isTableExists($sTable)
  515. {
  516. $aTableNames = $this->listTables();
  517. foreach($aTableNames as $iKey => $sTableName)
  518. $aTableNames[$iKey] = strtoupper($sTableName);
  519. return in_array(strtoupper($sTable), $aTableNames);
  520. }
  521. public function isFieldExists($sTable, $sFieldName)
  522. {
  523. $aFields = $this->getFields($sTable);
  524. return in_array(strtoupper($sFieldName), $aFields['uppercase']);
  525. }
  526. public function isIndexExists($sTable, $sIndexName)
  527. {
  528. $bIndex = false;
  529. $aIndexes = $this->getAll("SHOW INDEXES FROM `" . $sTable . "`");
  530. foreach($aIndexes as $aIndex)
  531. if($aIndex['Key_name'] == $sIndexName) {
  532. $bIndex = true;
  533. break;
  534. }
  535. return $bIndex;
  536. }
  537. public function error($aError)
  538. {
  539. $sErrorType = self::$_aErrors[$aError['code']];
  540. $bVerbose = isset($aError['verbose']) ? (bool)$aError['verbose'] : $this->_bErrorChecking;
  541. if(!$bVerbose) {
  542. $this->log($sErrorType . ': ' . $aError['message']);
  543. if (!defined('BX_DOL_INSTALL')) // this is needed to display error during installation
  544. return;
  545. }
  546. if((defined('BX_DB_FULL_VISUAL_PROCESSING') && BX_DB_FULL_VISUAL_PROCESSING) || defined('BX_DOL_INSTALL')) {
  547. $sOutput = '<div style="border:2px solid red;padding:4px;width:600px;margin:0px auto;">';
  548. $sOutput .= '<div style="text-align:center;background-color:red;color:white;font-weight:bold;">Error</div>';
  549. $sOutput .= '<div style="text-align:center;">' . $sErrorType . '</div>';
  550. if((defined('BX_DB_FULL_DEBUG_MODE') && BX_DB_FULL_DEBUG_MODE) || defined('BX_DOL_INSTALL'))
  551. $sOutput .= $this->errorOutput($aError);
  552. $sOutput .= '</div>';
  553. }
  554. if(defined('BX_DB_DO_EMAIL_ERROR_REPORT') && BX_DB_DO_EMAIL_ERROR_REPORT) {
  555. $sSiteTitle = $this->getParam('site_title');
  556. $sMailBody = "Database error in " . $sSiteTitle . "<br /><br /> \n";
  557. $sMailBody .= $this->errorOutput($aError);
  558. $sMailBody .= "<hr />Auto-report system";
  559. sendMail($this->getParam('site_email'), "Database error in " . $sSiteTitle, $sMailBody, 0, array(), BX_EMAIL_SYSTEM, 'html', true);
  560. }
  561. bx_show_service_unavailable_error_and_exit($sOutput);
  562. }
  563. protected function isParamInCache($sKey)
  564. {
  565. return is_array(self::$_aParams) && isset(self::$_aParams[$sKey]);
  566. }
  567. public function cacheParams($bForceCacheInvalidate = false, $bForceCacheInvalidateMixed = false)
  568. {
  569. if ($bForceCacheInvalidate)
  570. $this->cacheParamsClear();
  571. self::$_aParams = $this->fromCache(self::$_sParamsCacheName, 'getPairs', "SELECT `name`, `value` FROM `sys_options`", "name", "value");
  572. list($sTmplCode, $sTmplName) = BxDolTemplate::retrieveCode();
  573. if(!empty($sTmplCode) && !empty($sTmplName)) {
  574. $iTmplMix = 0;
  575. if(is_array($sTmplCode))
  576. list($sTmplCode, $iTmplMix) = $sTmplCode;
  577. if(empty($iTmplMix))
  578. $iTmplMix = (int)$this->getParam($sTmplName . '_default_mix');
  579. if(!empty($iTmplMix)) {
  580. $sCacheNameMixed = self::$_sParamsCacheNameMixed . $sTmplCode . '_' . $iTmplMix;
  581. if($bForceCacheInvalidateMixed)
  582. $this->cacheParamsClear($sCacheNameMixed);
  583. $aMixed = $this->fromCache($sCacheNameMixed, 'getPairs', "SELECT `option`, `value` FROM `sys_options_mixes2options` WHERE `mix_id`=:mix_id", "option", "value", array(
  584. 'mix_id' => $iTmplMix
  585. ));
  586. if(!empty($aMixed) && is_array($aMixed))
  587. self::$_aParams = array_merge(self::$_aParams, $aMixed);
  588. }
  589. }
  590. if (empty(self::$_aParams)) {
  591. self::$_aParams = array();
  592. return false;
  593. }
  594. return true;
  595. }
  596. public function cacheParamsClear($sCacheName = '')
  597. {
  598. if(empty($sCacheName))
  599. $sCacheName = self::$_sParamsCacheName;
  600. return $this->cleanCache($sCacheName);
  601. }
  602. public function isParam($sKey, $bFromCache = true)
  603. {
  604. if ($bFromCache && $this->isParamInCache($sKey))
  605. return true;
  606. $sQuery = $this->prepare("SELECT `name` FROM `sys_options` WHERE `name` = ? LIMIT 1", $sKey);
  607. return $this->getOne($sQuery) == $sKey;
  608. }
  609. public function addParam($sName, $sValue, $iKateg, $sDesc, $sType)
  610. {
  611. $sQuery = $this->prepare("INSERT INTO `sys_options` SET `category_id` = ?, `name` = ?, `caption` = ?, `value` = ?, `type` = ?", $iKateg, $sName, $sDesc, $sValue, $sType);
  612. $this->query($sQuery);
  613. // renew params cache
  614. $this->cacheParams(true);
  615. }
  616. public function getParam($sKey, $bFromCache = true)
  617. {
  618. if (!$sKey)
  619. return false;
  620. if ($bFromCache && $this->isParamInCache($sKey)) {
  621. return self::$_aParams[$sKey];
  622. } else {
  623. $sQuery = $this->prepare("SELECT `tmo`.`value` AS `value` FROM `sys_options_mixes2options` AS `tmo` INNER JOIN `sys_options_mixes` AS `tm` ON `tmo`.`mix_id`=`tm`.`id` AND `tm`.`active`='1' WHERE `tmo`.`option`=? LIMIT 1", $sKey);
  624. $mixedValue = $this->getOne($sQuery);
  625. if($mixedValue !== false)
  626. return $mixedValue;
  627. $sQuery = $this->prepare("SELECT `value` FROM `sys_options` WHERE `name` = ? LIMIT 1", $sKey);
  628. return $this->getOne($sQuery);
  629. }
  630. }
  631. public function setParam($sKey, $mixedValue, $iMixId = 0)
  632. {
  633. if(empty($iMixId))
  634. $sQuery = $this->prepare("UPDATE `sys_options` SET `value` = ? WHERE `name` = ? LIMIT 1", $mixedValue, $sKey);
  635. else
  636. $sQuery = $this->prepare("REPLACE INTO `sys_options_mixes2options` SET `option` = ?, `mix_id` = ?, `value` = ?", $sKey, $iMixId, $mixedValue);
  637. $bResult = (int)$this->query($sQuery) > 0;
  638. // renew params cache
  639. $bResult &= $this->cacheParams(true, !empty($iMixId));
  640. return $bResult;
  641. }
  642. public function getParamsMix($iId)
  643. {
  644. return $this->fromCache(self::$_sParamsCacheNameMix . $iId, 'getRow', 'SELECT * FROM `sys_options_mixes` WHERE `id`=:id', array(
  645. 'id' => $iId
  646. ));
  647. }
  648. public function getParamsMixActive($sType)
  649. {
  650. return $this->getRow("SELECT * FROM `sys_options_mixes` WHERE `type`=:type AND `active`='1'", array(
  651. 'type' => $sType
  652. ));
  653. }
  654. public function getParamsMixes($sType, $mixedPublished = false)
  655. {
  656. $aBindings = array('type' => $sType);
  657. $sQuery = "SELECT * FROM `sys_options_mixes` WHERE `type`=:type";
  658. if($mixedPublished !== false) {
  659. $aBindings['published'] = (int)$mixedPublished;
  660. $sQuery .= " AND `published`=:published";
  661. }
  662. return $this->getAll($sQuery, $aBindings);
  663. }
  664. public function setTimezone($sTimezone)
  665. {
  666. $oDate = new DateTime('now', new DateTimeZone($sTimezone));
  667. return $this->pdoExec('SET time_zone = "' . $oDate->format('P') . '"') !== false;
  668. }
  669. public function getEncoding()
  670. {
  671. $oStatement = $this->pdoQuery('SELECT @@character_set_database');
  672. if($oStatement !== false)
  673. return $this->getOne($oStatement);
  674. return false;
  675. }
  676. public function setErrorChecking ($b)
  677. {
  678. $this->_bErrorChecking = $b;
  679. }
  680. /**
  681. * Cache functions.
  682. */
  683. public function getDbCacheObject ()
  684. {
  685. if($this->_oDbCacheObject != null)
  686. return $this->_oDbCacheObject;
  687. $sEngine = $this->getParam('sys_db_cache_engine');
  688. $this->_oDbCacheObject = bx_instance('BxDolCache'.$sEngine);
  689. if(!$this->_oDbCacheObject->isAvailable())
  690. $this->_oDbCacheObject = bx_instance('BxDolCacheFile');
  691. return $this->_oDbCacheObject;
  692. }
  693. public function genDbCacheKey ($sName)
  694. {
  695. return 'db_' . $sName . '_' . bx_site_hash() . '.php';
  696. }
  697. public function fromCache ($sName, $sFunc)
  698. {
  699. $aArgs = func_get_args();
  700. array_shift ($aArgs); // shift $sName
  701. array_shift ($aArgs); // shift $sFunc
  702. if (!$this->getParam('sys_db_cache_enable'))
  703. return call_user_func_array (array ($this, $sFunc), $aArgs); // pass other function parameters as database function parameters
  704. $oCache = $this->getDbCacheObject ();
  705. $sKey = $this->genDbCacheKey($sName);
  706. $mixedRet = $oCache->getData($sKey);
  707. if ($mixedRet !== null) {
  708. return $mixedRet;
  709. } else {
  710. $mixedRet = call_user_func_array (array ($this, $sFunc), $aArgs); // pass other function parameters as database function parameters
  711. $oCache->setData($sKey, $mixedRet);
  712. }
  713. return $mixedRet;
  714. }
  715. public function cleanCache ($sName)
  716. {
  717. $oCache = $this->getDbCacheObject();
  718. $sKey = $this->genDbCacheKey($sName);
  719. return $oCache->delData($sKey);
  720. }
  721. public function &fromMemory ($sName, $sFunc)
  722. {
  723. if(array_key_exists($sName, self::$_aDbCacheData) && !defined('BX_DOL_INSTALL') && !defined('BX_DOL_CRON_EXECUTE'))
  724. return self::$_aDbCacheData[$sName];
  725. $aArgs = func_get_args();
  726. array_shift($aArgs); // shift $sName
  727. array_shift($aArgs); // shift $sFunc
  728. self::$_aDbCacheData[$sName] = call_user_func_array (array ($this, $sFunc), $aArgs); // pass other function parameters as database function parameters
  729. return self::$_aDbCacheData[$sName];
  730. }
  731. public function cleanMemory ($sName)
  732. {
  733. if(!isset(self::$_aDbCacheData[$sName]))
  734. return false;
  735. unset(self::$_aDbCacheData[$sName]);
  736. return true;
  737. }
  738. public function cleanMemoryAll ()
  739. {
  740. self::$_aDbCacheData = array();
  741. return true;
  742. }
  743. /**
  744. * It escapes string to pass to mysql query.
  745. * Try to use "prepare" function always (@see BxDolDb::prepare), use "escape" only if "prepare" function is not possible at all.
  746. * Also consider using "implode_escape" function (@see BxDolDb::implode_escape).
  747. *
  748. * @param string $s string to escape
  749. * @return escaped string whcich is ready to pass to SQL query.
  750. */
  751. public function escape($s)
  752. {
  753. try {
  754. $s = self::$_rLink->quote($s);
  755. }
  756. catch (PDOException $oException) {
  757. $oException->errorInfo[self::$_sErrorKey] = array(
  758. 'code' => BX_DB_ERR_ESCAPE,
  759. 'message' => $oException->getMessage(),
  760. 'trace' => $oException->getTrace()
  761. );
  762. throw $oException;
  763. }
  764. return $s;
  765. }
  766. /**
  767. * This function is usefull when you need to form array of parameters to pass to IN(...) SQL construction.
  768. * Example:
  769. * @code
  770. * $a = array(2, 4.5, 'apple', 'car');
  771. * $s = "SELECT * FROM `t` WHERE `a` IN (" . $oDb->implode_escape($a) . ")";
  772. * echo $s; // outputs: SELECT * FROM `t` WHERE `a` IN (2, 4.5, 'apple', 'car')
  773. * @endcode
  774. *
  775. * @param $mixed array or parameters or just one paramter
  776. * @return string which is ready to pass to IN(...) SQL construction
  777. */
  778. public function implode_escape ($mixed)
  779. {
  780. if (is_array($mixed)) {
  781. $s = '';
  782. foreach ($mixed as $v)
  783. $s .= (is_numeric($v) ? $v : $this->escape($v)) . ',';
  784. if ($s)
  785. return substr($s, 0, -1);
  786. else
  787. return 'NULL';
  788. }
  789. return is_numeric($mixed) ? $mixed : ($mixed ? $this->escape($mixed) : 'NULL');
  790. }
  791. /**
  792. * @deprecated
  793. */
  794. public function unescape ($mixed)
  795. {
  796. if(is_array($mixed)) {
  797. foreach($mixed as $k => $v)
  798. $mixed[$k] = $this->getOne("SELECT '$v'");
  799. return $mixed;
  800. }
  801. else
  802. return $this->getOne("SELECT '$mixed'");
  803. }
  804. /**
  805. * Prepare SQL query before execution if some arguments are need to be passed to it.
  806. * All parameters marked with question (?) symbol in SQL query are replaced with parameters passed after SQL query parameter.
  807. * Parameters are properly excaped and surrounded by qutes if needed.
  808. * Example:
  809. * @code
  810. * $sSql = $oDb->prepare("SELECT `a`, `b` from `t` WHERE `c` = ? and `d` = ?", 12, 'aa');
  811. * echo $sSql;// outputs: SELECT `a`, `b` from `t` WHERE `c` = 12 and `d` = 'aa'
  812. * $a = $oDb->getAll($sSql);
  813. * @endcode
  814. *
  815. * @param string $sQuery SQL query, parameters for replacing are marked with ? symbol
  816. * @param mixed $mixed any number if parameters to replace, number of parameters whould match number of ? symbols in SQL query
  817. * @return PDOStatement object with SQL query ready for execution
  818. */
  819. public function prepare($sQuery)
  820. {
  821. if(!self::$_rLink)
  822. return false;
  823. if (self::$_bMultuServersMode && (0 != strncasecmp(ltrim($sQuery, " \t\n\r(\0\x0B"), 'SELECT', 6)))
  824. $this->setReadOnlyMode(false);
  825. $aArgs = func_get_args();
  826. $sQuery = array_shift($aArgs);
  827. $oStatement = self::$_rLink->prepare($sQuery);
  828. $iIndex = 1;
  829. foreach($aArgs as $mixedArg) {
  830. if(is_null($mixedArg))
  831. $iValueType = PDO::PARAM_NULL;
  832. else if(is_numeric($mixedArg) && is_int($mixedArg))
  833. $iValueType = PDO::PARAM_INT;
  834. else
  835. $iValueType = PDO::PARAM_STR;
  836. $oStatement->bindValue($iIndex++, $mixedArg, $iValueType);
  837. }
  838. return $oStatement;
  839. }
  840. /**
  841. * Prepare SQL query before execution if some arguments are need to be passed to it.
  842. * All parameters marked with question (?) symbol in SQL query are replaced with parameters passed after SQL query parameter.
  843. * Parameters are properly excaped and surrounded by qutes if needed.
  844. * Example:
  845. * @code
  846. * $sSql = $oDb->prepare("SELECT `a`, `b` from `t` WHERE `c` = ? and `d` = ?", 12, 'aa');
  847. * echo $sSql;// outputs: SELECT `a`, `b` from `t` WHERE `c` = 12 and `d` = 'aa'
  848. * $a = $oDb->getAll($sSql);
  849. * @endcode
  850. *
  851. * @param string $sQuery SQL query, parameters for replacing are marked with ? symbol
  852. * @param mixed $mixed any number if parameters to replace, number of parameters whould match number of ? symbols in SQL query
  853. * @return string with SQL query.
  854. */
  855. function prepareAsString($sQuery)
  856. {
  857. $aArgs = func_get_args();
  858. $sQuery = array_shift($aArgs);
  859. $iPos = 0;
  860. foreach ($aArgs as $mixedArg) {
  861. if (is_null($mixedArg))
  862. $s = 'NULL';
  863. elseif (is_numeric($mixedArg))
  864. $s = $mixedArg;
  865. else
  866. $s = $this->escape($mixedArg);
  867. $i = bx_mb_strpos($sQuery, '?', $iPos);
  868. $sQuery = bx_mb_substr_replace($sQuery, $s, $i, 1);
  869. $iPos = $i + get_mb_len($s);
  870. }
  871. return $sQuery;
  872. }
  873. /**
  874. * Convert array of key => values to SQL query.
  875. * Array keys are field names and array values are field values.
  876. * @param $a array
  877. * @param $sDiv fields separator, by default it is ',', another useful value is ' AND '
  878. * @return part of SQL query string
  879. */
  880. public function arrayToSQL($a, $sDiv = ',')
  881. {
  882. $s = '';
  883. foreach($a as $k => $v)
  884. $s .= "`{$k}` = " . $this->escape($v) . $sDiv;
  885. return trim($s, $sDiv);
  886. }
  887. protected function log($s)
  888. {
  889. if (defined('BX_DIRECTORY_PATH_LOGS')) {
  890. $sPath = BX_DIRECTORY_PATH_LOGS;
  891. }
  892. else {
  893. $sDirName = pathinfo(__FILE__, PATHINFO_DIRNAME);
  894. $sPath = $sDirName . '/../../logs/';
  895. }
  896. return file_put_contents($sPath . 'db.err.log', date('Y-m-d H:i:s') . "\t" . $s . "\n", FILE_APPEND);
  897. }
  898. public function executeSQL($sPath, $aReplace = array (), $isBreakOnError = true)
  899. {
  900. if(!file_exists($sPath) || !($rHandler = fopen($sPath, "r")))
  901. return array(array ('query' => "fopen($sPath, 'r')", 'error' => 'file not found or permission denied'));
  902. self::$_rLink->setAttribute(PDO::ATTR_ERRMODE, BX_DB_MODE_SILENT);
  903. $sQuery = "";
  904. $sDelimiter = ';';
  905. $aResult = array();
  906. while(!feof($rHandler)) {
  907. $sStr = trim(fgets($rHandler));
  908. if(empty($sStr) || $sStr[0] == "" || $sStr[0] == "#" || ($sStr[0] == "-" && $sStr[1] == "-"))
  909. continue;
  910. //--- Change delimiter ---//
  911. if(strpos($sStr, "DELIMITER //") !== false || strpos($sStr, "DELIMITER ;") !== false) {
  912. $sDelimiter = trim(str_replace('DELIMITER', '', $sStr));
  913. continue;
  914. }
  915. $sQuery .= $sStr;
  916. //--- Check for multiline query ---//
  917. if(substr($sStr, -strlen($sDelimiter)) != $sDelimiter)
  918. continue;
  919. //--- Execute query ---//
  920. if ($aReplace)
  921. $sQuery = str_replace($aReplace['from'], $aReplace['to'], $sQuery);
  922. if($sDelimiter != ';')
  923. $sQuery = str_replace($sDelimiter, "", $sQuery);
  924. if($this->query(trim($sQuery), array(), false) === false) {
  925. $aResult[] = array('query' => $sQuery, 'error' => $this->_aError['message']);
  926. if ($isBreakOnError)
  927. break;
  928. }
  929. $sQuery = "";
  930. }
  931. fclose($rHandler);
  932. self::$_rLink->setAttribute(PDO::ATTR_ERRMODE, $this->_iPdoErrorMode);
  933. return empty($aResult) ? true : $aResult;
  934. }
  935. protected function executeStatement($oStatement, $aBindings = array(), $bVerbose = null, &$iErrorCode = null)
  936. {
  937. $bResult = false;
  938. if (!isset($aBindings[0])) {
  939. foreach($aBindings as $sKey => $mixedValue) {
  940. if(is_null($mixedValue))
  941. $oStatement->bindValue(":{$sKey}", $mixedValue, PDO::PARAM_NULL);
  942. else if(is_numeric($mixedValue) && is_int($mixedValue))
  943. $oStatement->bindValue(":{$sKey}", $mixedValue, PDO::PARAM_INT);
  944. else
  945. $oStatement->bindValue(":{$sKey}", $mixedValue, PDO::PARAM_STR);
  946. }
  947. $aBindings = array();
  948. }
  949. switch (self::$_rLink->getAttribute(PDO::ATTR_ERRMODE)) {
  950. case PDO::ERRMODE_SILENT:
  951. $bResult = $this->executeStatementSilent($oStatement, $aBindings, $bVerbose);
  952. break;
  953. case PDO::ERRMODE_EXCEPTION:
  954. $bResult = $this->executeStatementException($oStatement, $aBindings, $bVerbose);
  955. break;
  956. }
  957. if (!$bResult && !is_null($iErrorCode)) {
  958. $aError = $oStatement->errorInfo();
  959. $iErrorCode = $aError[1];
  960. }
  961. return $bResult;
  962. }
  963. protected function executeStatementException($oStatement, $aBindings = array(), $bVerbose = null)
  964. {
  965. $bResult = false;
  966. try {
  967. $bResult = $oStatement->execute(!empty($aBindings) && is_array($aBindings) ? $aBindings : null);
  968. }
  969. catch (PDOException $oException) {
  970. $aError = $oStatement->errorInfo();
  971. $oException->errorInfo[self::$_sErrorKey] = array(
  972. 'code' => BX_DB_ERR_QUERY_ERROR,
  973. 'message' => !empty($aError[2]) ? $aError[2] : $oException->getMessage(),
  974. 'query' => $oStatement->queryString,
  975. 'trace' => $oException->getTrace(),
  976. 'verbose' => $bVerbose
  977. );
  978. throw $oException;
  979. }
  980. return $bResult;
  981. }
  982. protected function executeStatementSilent($oStatement, $aBindings = array(), $bVerbose = null)
  983. {
  984. $bResult = $oStatement->execute(!empty($aBindings) && is_array($aBindings) ? $aBindings : null);
  985. if($bResult)
  986. return true;
  987. $aError = $oStatement->errorInfo();
  988. $aTrace = debug_backtrace();
  989. unset($aTrace[0]);
  990. $this->_aError = array(
  991. 'code' => BX_DB_ERR_QUERY_ERROR,
  992. 'message' => !empty($aError[2]) ? $aError[2] : '',
  993. 'query' => $oStatement->queryString,
  994. 'trace' => $aTrace,
  995. 'verbose' => $bVerbose
  996. );
  997. return false;
  998. }
  999. protected function errorOutput($aError)
  1000. {
  1001. $aErrorLocation = array();
  1002. if(!empty($aError['query']) && !empty($aError['trace']))
  1003. foreach($aError['trace'] as $aCall )
  1004. if(isset($aCall['args']) && is_array($aCall['args']))
  1005. foreach($aCall['args'] as $argNum => $argVal)
  1006. if((is_string($argVal) && strcmp($argVal, $aError['query']) == 0) || ($argVal instanceof PDOStatement && strcmp($argVal->queryString, $aError['query']) == 0)) {
  1007. $aErrorLocation['file'] = isset($aCall['file']) ? $aCall['file'] : (isset($aCall['class']) ? 'class: ' . $aCall['class'] : 'undefined');
  1008. $aErrorLocation['line'] = isset($aCall['line']) ? $aCall['line'] : 'undefined';
  1009. $aErrorLocation['function'] = $aCall['function'];
  1010. $aErrorLocation['arg'] = $argNum;
  1011. }
  1012. $sOutput = '';
  1013. if(!empty($aError['query']))
  1014. $sOutput .= '<p><b>Query:</b><br />' . $aError['query'] . '</p>';
  1015. if(!empty($aError['message']))
  1016. $sOutput .= '<p><b>Mysql error:</b><br />' . $aError['message'] . '</p>';
  1017. if(!empty($aErrorLocation))
  1018. $sOutput .= '<p><b>Location:</b><br />The error was found in <b>' . $aErrorLocation['function'] . '</b> function in the file <b>' . $aErrorLocation['file'] . '</b> at line <b>' . $aErrorLocation['line'] . '</b>.</p>';
  1019. $sOutput .= '<p><b>collation_connection:</b><br />' . $this->getOne("SELECT @@collation_connection") . '</p>';
  1020. if(!empty($aError['trace'])) {
  1021. $sBackTrace = print_r($aError['trace'], true);
  1022. if (defined ('BX_DATABASE_USER') && !is_array(BX_DATABASE_USER))
  1023. $sBackTrace = str_replace('[_sUser:protected] => ' . BX_DATABASE_USER, '[_sUser:protected] => *****', $sBackTrace);
  1024. if (defined ('BX_DATABASE_PASS') && !is_array(BX_DATABASE_PASS))
  1025. $sBackTrace = str_replace('[_sPassword:protected] => ' . BX_DATABASE_PASS, '[_sPassword:protected] => *****', $sBackTrace);
  1026. $sOutput .= '<div><b>Debug backtrace:</b></div><div style="overflow:scroll;height:300px;border:1px solid gray;"><pre>' . htmlspecialchars_adv($sBackTrace) . '</pre></div>';
  1027. }
  1028. if(!empty(self::$_aParams)) {
  1029. $sSettings = var_export(self::$_aParams, true);
  1030. $sOutput .= '<div><b>Settings:</b></div><div style="overflow:scroll;height:300px;border:1px solid gray;"><pre>' . htmlspecialchars_adv($sSettings) . '</pre></div>';
  1031. }
  1032. $sOutput .= '<p><b>Called script:</b><br />' . $_SERVER['PHP_SELF'] . '</p>';
  1033. if(!empty($_REQUEST)) {
  1034. $sRequest = var_export($_REQUEST, true);
  1035. $sOutput .= '<p><b>Request parameters:</b><br /><pre>' . htmlspecialchars_adv($sRequest) . '</pre></p>';
  1036. }
  1037. return $sOutput;
  1038. }
  1039. }
  1040. /**
  1041. * Create the very first instance and initiate connetion to database.
  1042. */
  1043. BxDolDb::getInstance();
  1044. function getParam($sParamName, $bUseCache = true)
  1045. {
  1046. return BxDolDb::getInstance()->getParam($sParamName, $bUseCache);
  1047. }
  1048. function setParam($sParamName, $sParamVal)
  1049. {
  1050. return BxDolDb::getInstance()->setParam($sParamName, $sParamVal);
  1051. }
  1052. /** @} */