diff --git a/config/config.php b/config/config.php index 700252d255..f9db6dccfb 100644 --- a/config/config.php +++ b/config/config.php @@ -66,11 +66,8 @@ $config->db->driver = 'mysql'; // 目前只支持MySQL数据库。Mus $config->db->encoding = 'UTF8'; // 数据库编码。 Encoding of database. $config->db->strictMode = true; // 默认开启MySQL的严格模式。 Turn on the strict mode of MySQL. $config->db->prefix = 'zt_'; // 数据库表名前缀。 The prefix of the table name. -$config->db->enableSqlite = false; // 是否启用SQLite Enable SQLite or not. -$config->slaveDB->persistent = false; -$config->slaveDB->driver = 'mysql'; -$config->slaveDB->encoding = 'UTF8'; -$config->slaveDB->strictMode = true; +$config->db->enableSqlite = false; // 是否启用SQLite Enable SQLite or not. +$config->slaveDBList = array(); // 支持多个从库。 Support multiple slave dbs. /* 可用域名后缀列表。Domain postfix lists. */ $config->domainPostfix = "|com|com.cn|com.hk|com.tw|com.vc|edu.cn|es|"; diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 3905c9a736..a25c7ad2cd 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -729,6 +729,7 @@ class baseRouter $_POST = validater::filterSuper($_POST); $_GET = validater::filterSuper($_GET); $_COOKIE = validater::filterSuper($_COOKIE); + $_SERVER = validater::filterSuper($_SERVER); /* Filter common get and cookie vars. */ if($this->config->framework->filterParam == 2) @@ -856,10 +857,10 @@ class baseRouter { $sql = new sql(); $account = $sql->quote($account); - $vision = $this->dbh->query("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch(); + $vision = $this->dbQuery("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch(); if($vision) $vision = $vision->value; - $user = $this->dbh->query("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch(); + $user = $this->dbQuery("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch(); if(!empty($user->visions)) { $userVisions = explode(',', (string) $user->visions); @@ -882,7 +883,7 @@ class baseRouter */ public function getInstalledVersion() { - $version = $this->dbh->query("SELECT `value` FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch(); + $version = $this->dbQuery("SELECT `value` FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch(); $version = $version ? $version->value : '0.3.beta'; // No version, set as 0.3.beta. if($version == '3.0.stable') $version = '3.0'; // convert 3.0.stable to 3.0. return $version; @@ -2889,8 +2890,50 @@ class baseRouter global $config, $dbh, $slaveDBH; if(!isset($config->installed) or !$config->installed) return; - if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db); - if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB); + /* Set master db. */ + if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db, 'MASTER'); + + /* Set slave db. */ + if(empty($config->slaveDBList)) return; + + $biIndex = 0; + $slaveList = array(); + foreach($config->slaveDBList as $index => $db) + { + if(isset($db->type) && $db->type == 'bi') + { + $biIndex = $index; + } + else + { + $slaveList[] = $index; + } + } + $slaveIndex = empty($slaveList) ? $biIndex : $slaveList[array_rand($slaveList)]; + + $config->biDB = $this->initSlaveDB($biIndex); + $this->slaveDBH = $slaveDBH = $this->connectByPDO($this->initSlaveDB($slaveIndex), 'SLAVE'); + } + + /** + * Init config of slave db. + * + * @param int $slaveIndex + * @access private + * @return object + */ + private function initSlaveDB(int $slaveIndex = 0) + { + global $config; + + $slaveDB = $config->slaveDBList[$slaveIndex]; + $slaveDB->persistant = $config->db->persistent; + $slaveDB->driver = $config->db->driver; + $slaveDB->encoding = $config->db->encoding; + $slaveDB->strictMode = $config->db->strictMode; + $slaveDB->prefix = $config->db->prefix; + + return $slaveDB; } /** @@ -2898,16 +2941,17 @@ class baseRouter * Connect database by PDO. * * @param object $params the database params. + * @param string $flag the database flag. * @access public * @return object|bool */ - public function connectByPDO(object $params): object|bool + public function connectByPDO(object $params, $flag = 'MASTER'): object|bool { if(!isset($params->driver)) static::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, true); if(!isset($params->user)) return false; try { - $dbh = new dbh($params); + $dbh = new dbh($params, true, $flag); $dbh->exec("SET NAMES {$params->encoding}"); /* @@ -2960,6 +3004,21 @@ class baseRouter return new sqlite($params); } + /** + * Query from database, use master or slave db. + * + * @param string $query + * @access public + * @return mixed + */ + public function dbQuery($query) + { + if(!$this->dbh) return false; + if($this->slaveDBH) return $this->slaveDBH->query($query); + + return $this->dbh->query($query); + } + //-------------------- 错误处理方法(Error methods) ------------------// /** diff --git a/framework/model.class.php b/framework/model.class.php index d8db052415..c1816165a1 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -410,4 +410,25 @@ class model extends baseModel $app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, true); } + + /** + * Load dao of bi. + * + * @access public + * @return void + */ + public function loadBIDAO() + { + global $config, $biDAO; + if(is_object($biDAO)) return $this->dao = $biDAO; + + if(!isset($config->biDB)) return; + + $driver = $config->db->driver; + $biDAO = new $driver(); + + $biDAO->slaveDBH = $this->app->connectByPDO($config->biDB, 'BI'); + + $this->dao = $biDAO; + } } diff --git a/framework/router.class.php b/framework/router.class.php index 6692ccddbb..a7bd03d858 100755 --- a/framework/router.class.php +++ b/framework/router.class.php @@ -106,7 +106,7 @@ class router extends baseRouter { if($this->dbh) { - $langs = $this->dbh->query('SELECT `value` FROM ' . TABLE_CONFIG . " WHERE `owner`='system' AND `module`='common' AND `section`='global' AND `key`='langs'")->fetch(); + $langs = $this->dbQuery('SELECT `value` FROM ' . TABLE_CONFIG . " WHERE `owner`='system' AND `module`='common' AND `section`='global' AND `key`='langs'")->fetch(); $langs = empty($langs) ? array() : json_decode($langs->value, true); foreach($langs as $langKey => $langData) $this->config->langs[$langKey] = $langData['name']; } @@ -140,7 +140,7 @@ class router extends baseRouter $customMenus = array(); try { - $customMenus = $this->dbh->query('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `section`='mainNav' AND `lang`='{$this->clientLang}' AND `vision`='{$this->config->vision}'")->fetchAll(); + $customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `section`='mainNav' AND `lang`='{$this->clientLang}' AND `vision`='{$this->config->vision}'")->fetchAll(); } catch(PDOException){} @@ -234,7 +234,7 @@ class router extends baseRouter try { - $commonSettings = $this->dbh->query('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll(); + $commonSettings = $this->dbQuery('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll(); } catch (PDOException $exception) { @@ -302,7 +302,7 @@ class router extends baseRouter { $sql = new sql(); $account = $sql->quote($account); - $userSetting = $this->dbh->query('SELECT `key`, `value` FROM ' . TABLE_CONFIG . " WHERE `owner`= $account AND `module`='common' and `key` in ('programLink', 'productLink', 'projectLink', 'executionLink', 'URSR')")->fetchAll(); + $userSetting = $this->dbQuery('SELECT `key`, `value` FROM ' . TABLE_CONFIG . " WHERE `owner`= $account AND `module`='common' and `key` in ('programLink', 'productLink', 'projectLink', 'executionLink', 'URSR')")->fetchAll(); } foreach($userSetting as $setting) @@ -318,7 +318,7 @@ class router extends baseRouter $lang->SRCommon = ''; if($this->dbh and !empty($this->config->db->name)) { - $productProject = $this->dbh->query('SELECT `value` FROM ' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch(); + $productProject = $this->dbQuery('SELECT `value` FROM ' . TABLE_CONFIG . "WHERE `owner`='system' AND `module`='custom' AND `key`='productProject'")->fetch(); if(is_string($productProject)) { list($productCommon, $projectCommon) = explode('_', $productProject); @@ -328,8 +328,8 @@ class router extends baseRouter { /* Get story concept in project and product. */ $clientLang = $this->clientLang == 'zh-tw' ? 'zh-cn' : $this->clientLang; - $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE `module` = 'custom' and `section` = 'URSRList' and `lang` = '{$clientLang}'")->fetchAll(); - if(empty($URSRList)) $URSRList = $this->dbh->query('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll(); + $URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE `module` = 'custom' and `section` = 'URSRList' and `lang` = '{$clientLang}'")->fetchAll(); + if(empty($URSRList)) $URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll(); /* Get UR pairs and SR pairs. */ $URPairs = array(); @@ -350,7 +350,7 @@ class router extends baseRouter $customMenus = array(); try { - $customMenus = $this->dbh->query('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll(); + $customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll(); } catch(PDOException){} foreach($customMenus as $menu) if(isset($lang->{$menu->key})) $lang->{$menu->key} = $menu->value; @@ -507,7 +507,7 @@ class router extends baseRouter if($this->config->edition == 'open' or $this->installing or $this->upgrading) return parent::setControlFile($exitIfNone); /* Check if the requested module is defined in workflow. */ - $flow = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOW . " WHERE `module` = '$this->moduleName'")->fetch(); + $flow = $this->dbQuery("SELECT * FROM " . TABLE_WORKFLOW . " WHERE `module` = '$this->moduleName'")->fetch(); if(!$flow) return parent::setControlFile($exitIfNone); if($flow->status != 'normal') helper::end("
{$this->lang->flowNotRelease}"); @@ -532,7 +532,7 @@ class router extends baseRouter } else { - $action = $this->dbh->query("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '$this->moduleName' AND `action` = '$this->methodName' AND `vision` = '{$this->config->vision}'")->fetch(); + $action = $this->dbQuery("SELECT * FROM " . TABLE_WORKFLOWACTION . " WHERE `module` = '$this->moduleName' AND `action` = '$this->methodName' AND `vision` = '{$this->config->vision}'")->fetch(); if(zget($action, 'extensionType') == 'override') { $this->rawModule = $this->moduleName; diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index aa5ad99b61..196640385f 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -360,9 +360,10 @@ class baseDAO */ public function descTable($tableName) { - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); - $fields = $this->query("DESC $tableName")->fetchAll(); - $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); + $dbh = $this->slaveDBH ? $this->slaveDBH : $this->dbh; + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + $fields = $dbh->rawQuery("DESC $tableName")->fetchAll(); + $dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL); return $fields; } @@ -415,7 +416,6 @@ class baseDAO if($orderPOS) $subLength = $orderPOS; if($groupPOS) $subLength = $groupPOS; $sql = substr($sql, 0, $subLength); - self::$querys[] = $sql; /* * 获取记录数。 @@ -423,7 +423,8 @@ class baseDAO **/ try { - $row = $this->dbh->rawQuery($sql)->fetch(PDO::FETCH_OBJ); + $dbh = $this->slaveDBH ? $this->slaveDBH : $this->dbh; + $row = $dbh->rawQuery($sql)->fetch(PDO::FETCH_OBJ); } catch (PDOException $e) { @@ -577,7 +578,7 @@ class baseDAO */ public function get() { - return $this->processKeywords($this->processSQL(false)); + return self::processKeywords($this->processSQL()); } /** @@ -617,7 +618,7 @@ class baseDAO * @access public * @return string the sql string after process. */ - public function processSQL($record = true) + public function processSQL() { $sql = $this->sqlobj->get(); @@ -694,7 +695,6 @@ class baseDAO } } - if($record) self::$querys[] = $this->processKeywords($sql); return $sql; } @@ -706,7 +706,7 @@ class baseDAO * @access public * @return string the sql string. */ - public function processKeywords($sql) + static public function processKeywords($sql) { return str_replace(array(DAO::WHERE, DAO::GROUPBY, DAO::HAVING, DAO::ORDERBY, DAO::LIMIT), array('WHERE', 'GROUP BY', 'HAVING', 'ORDER BY', 'LIMIT'), $sql); } @@ -760,12 +760,15 @@ class baseDAO $method = $this->method; $this->reset(); - if($this->slaveDBH and $method == 'select') + if($this->slaveDBH and in_array($method, array('select', 'desc'))) { return $this->slaveDBH->rawQuery($sql); } else { + /* Force to query from master db, if db has been changed. */ + $this->slaveDBH = false; + return $this->driver == 'sqlite' ? $this->dbh->query($sql) : $this->dbh->rawQuery($sql); } } @@ -861,6 +864,10 @@ class baseDAO { if($this->table) unset(dao::$cache[$this->table]); $this->reset(); + + /* Force to query from master db, if db has been changed. */ + $this->slaveDBH = false; + return $this->dbh->exec($sql); } catch (PDOException $e) diff --git a/lib/base/filter/filter.class.php b/lib/base/filter/filter.class.php index 7a4c77ea48..8a98daa905 100644 --- a/lib/base/filter/filter.class.php +++ b/lib/base/filter/filter.class.php @@ -661,11 +661,11 @@ class baseValidater global $config; if(empty($config->framework->filterXSS)) return $var; - if(stripos($var, '<script') !== false or stripos($var, '