<?php
/**
* ShopEx licence
*
* @copyright Copyright (c) 2005-2010 ShopEx Technologies Inc. (http://www.shopex.cn)
* @license http://ecos.shopex.cn/ ShopEx License
*/
class base_db_connections extends base_db_abstract implements base_interface_db
{
protected $_rw_lnk = null;
protected $_ro_lnk = null;
protected $_use_transaction = false;
protected $_in_transaction = false;
public $_enable_innodb = false;
public function __construct(){
parent::__construct();
}
public function enable_innodb(){
$status = false;
$rs = $this->select('SHOW ENGINES');
foreach($rs as $row){
if($row['Engine'] == 'InnoDB' && ($row['Support'] == 'YES' || $row['Support'] == 'DEFAULT') ){
$status = true;
}
}
return $this->_enable_innodb = $status;
}
public function exec($sql, $skipModifiedMark=false, $db_lnk=null){
if($this->prefix != 'sdb_'){
$sql = preg_replace_callback('/([`\s\(,])(sdb_)([0-9a-z\_]+)([`\s\.]{0,1})/is', array($this, 'fix_dbprefix'), $sql);
}
if(!$skipModifiedMark && !defined('DB_CACHE') && cachemgr::enable() && preg_match('/(?:(delete\s+from)|(insert\s+into)|(update))\s+([]0-9a-z_:"`.@[-]*)/is', $sql, $match)){
$table = strtoupper(trim(str_replace(array('`','"','\''), '', $match[4])));
$now = time();
$pos = strpos($table, strtoupper($this->prefix));
if($pos === 0){
$table = substr($table, strlen($this->prefix));
}
if(defined('BASE_CACHE_EXPIRES')){
preg_match("/([A-Z]*)_/", $table, $m);
$app_id = strtolower($m[1]);
$array = array('expire'=>$now, 'type'=>'DB', 'name'=>$table, 'app'=>$app_id);
base_kvstore::instance('cache/cache_expires')->store($table, $array);
cachemgr::set_modified('DB', $table, $now);
} else {
$this->exec('UPDATE sdb_base_cache_expires SET expire = "' . $now . '" WHERE type = "DB" AND name = "' . $table . '"', true);
if($this->affect_row()){
cachemgr::set_modified('DB', $table, $now);
}
}
}
if(!is_resource($db_lnk) && !($db_lnk instanceof mysqli)){
if($this->_rw_lnk){
$db_lnk = $this->_rw_lnk;
} else {
$db_lnk = $this->_rw_conn();
}
}
if(defined("STRESS_TESTING")){
b2c_forStressTest::$sqlAmount++;
b2c_forStressTest::slowSqlStart();
}
// mysqli_query 参数顺序:连接, sql
if($rs = mysqli_query($db_lnk, $sql)){
if(defined("STRESS_TESTING")){
b2c_forStressTest::slowSqlEnd($sql);
}
self::$mysql_query_executions++;
logger::debug('sql:'.self::$mysql_query_executions.'.'.$sql);
$db_result = array('rs' => $rs, 'sql' => $sql);
return $db_result;
} else {
logger::error($sql.':'.mysqli_error($db_lnk));
trigger_error($sql.':'.mysqli_error($db_lnk), E_USER_WARNING);
return false;
}
}
protected function fix_dbprefix($matches)
{
return $matches[1] . ((trim($matches[1])=='`') ? $this->prefix.$matches[3] : '`'.$this->prefix.$matches[3].'`') . $matches[4];
}
public function select($sql, $skipModifiedMark=false){
if($this->_rw_lnk){
$db_lnk = $this->_rw_lnk;
} else if($this->_ro_lnk){
$db_lnk = $this->_ro_lnk;
} else {
$db_lnk = $this->_ro_conn();
}
if($this->prefix != 'sdb_'){
$sql = preg_replace_callback('/([`\s\(,])(sdb_)([0-9a-z\_]+)([`\s\.]{0,1})/is', array($this, 'fix_dbprefix'), $sql);
}
if(cachemgr::enable() && cachemgr::check_current_co_depth() > 0 && preg_match_all('/FROM\s+([]0-9a-z_:"`.@[-]*)/is', $sql, $matches)){
if(isset($matches[1])){
foreach($matches[1] as $table){
if(empty($table)) continue;
$table = strtoupper(trim(str_replace(array('`','"','\''), '', $table)));
$pos = strpos($table, strtoupper($this->prefix));
if($pos === 0){
$table = substr($table, strlen($this->prefix));
}
if(!cachemgr::check_current_co_objects_exists('DB', $table)){
cachemgr::check_expires('DB', $table);
}
}
}
}
$rs = $this->exec($sql, $skipModifiedMark, $db_lnk);
if($rs && $rs['rs']){
$data = array();
while($row = mysqli_fetch_assoc($rs['rs'])){
$data[] = $row;
}
mysqli_free_result($rs['rs']);
return $data;
} else {
return false;
}
}
public function selectrow($sql){
$row = $this->selectlimit($sql, 1, 0);
return isset($row[0]) ? $row[0] : false;
}
public function selectlimit($sql, $limit=10, $offset=0){
if($offset >= 0 && $limit >= 0){
$offset = $offset >= 0 ? intval($offset) . ',' : '';
$limit = $limit >= 0 ? intval($limit) : '18446744073709551615';
$sql .= ' LIMIT ' . $offset . ' ' . $limit;
}
$data = $this->select($sql);
return $data;
}
protected function _ro_conn(){
if(defined('DB_SLAVE_HOST') && $this->_use_transaction !== true){
$this->_ro_lnk = $this->_connect(DB_SLAVE_HOST, DB_SLAVE_USER, DB_SLAVE_PASSWORD, DB_SLAVE_NAME);
} elseif($this->_rw_lnk){
$this->_ro_lnk = $this->_rw_lnk;
} else {
$this->_ro_lnk = $this->_rw_conn();
}
return $this->_ro_lnk;
}
public function getRows($rs, $row=10){
$i = 0;
$data = array();
while(($r = mysqli_fetch_assoc($rs['rs'])) && $i++ < $row){
$data[] = $r;
}
return $data;
}
protected function _rw_conn(){
$this->_rw_lnk = $this->_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
return $this->_rw_lnk;
}
protected function _connect($host, $user, $passwd, $dbname){
if(defined('DB_PCONNECT') && constant('DB_PCONNECT')){
// mysqli 没有持久连接函数,推荐用普通连接
$lnk = mysqli_connect($host, $user, $passwd);
} else {
$lnk = @mysqli_connect($host, $user, $passwd);
}
if(!$lnk && kernel::is_online()){
trigger_error(app::get('base')->_('无法连接数据库:').mysqli_connect_error(), E_USER_ERROR);
}
if(!mysqli_select_db($lnk, $dbname)){
trigger_error('Cannot select database '.$dbname.': '.mysqli_error($lnk), E_USER_ERROR);
}
$dbver = mysqli_get_server_info($lnk);
if(preg_match('/[0-9\.]+/', $dbver, $match)){
$dbverNum = $match[0];
if(version_compare($dbverNum, '4.1.1', '<')){
define('DB_OLDVERSION', 1);
$this->dbver = 3;
} else {
if(defined('DB_CHARSET') && constant('DB_CHARSET')){
mysqli_query($lnk, 'SET NAMES \'' . DB_CHARSET . '\'');
}
if(!version_compare($dbverNum, '6', '<')){
$this->dbver = 6;
}
}
}
return $lnk;
}
public function count($sql) {
$sql = preg_replace(array(
'/(.*\s)LIMIT .*/i',
'/^select\s+(.+?)\bfrom\b/is'
), array(
'\\1',
'select count(*) as c from'
), trim($sql));
$row = $this->select($sql);
return isset($row[0]['c']) ? intval($row[0]['c']) : 0;
}
protected function _whereClause($queryString){
preg_match('/\sWHERE\s(.*)/is', $queryString, $whereClause);
$discard = false;
if ($whereClause) {
if (preg_match('/\s(ORDER\s.*)/is', $whereClause[1], $discard));
else if (preg_match('/\s(LIMIT\s.*)/is', $whereClause[1], $discard));
else preg_match('/\s(FOR UPDATE.*)/is', $whereClause[1], $discard);
} else
$whereClause = array(false,false);
if ($discard)
$whereClause[1] = substr($whereClause[1], 0, strlen($whereClause[1]) - strlen($discard[1]));
return $whereClause[1];
}
public function quote($string){
if($this->_rw_lnk){
$result = mysqli_real_escape_string($this->_rw_lnk, $string);
} else {
// 这里没有数据库连接时,降级用 addslashes
$result = addslashes($string);
}
return "'".$result."'";
}
public function lastinsertid(){
$sql = 'SELECT LAST_INSERT_ID() AS lastinsertid';
$rs = $this->exec($sql, true, $this->_rw_lnk);
if($rs && $rs['rs']){
$row = mysqli_fetch_assoc($rs['rs']);
mysqli_free_result($rs['rs']);
return $row['lastinsertid'];
}
return 0;
}
public function affect_row(){
if($this->_rw_lnk){
$db_lnk = $this->_rw_lnk;
} else if($this->_ro_lnk){
$db_lnk = $this->_ro_lnk;
} else {
return 0;
}
return mysqli_affected_rows($db_lnk);
}
public function errorinfo(){
if($this->_rw_lnk){
$db_lnk = $this->_rw_lnk;
} else if($this->_ro_lnk){
$db_lnk = $this->_ro_lnk;
} else {
return '';
}
return mysqli_error($db_lnk);
}
public function errorcode()
{
if($this->_rw_lnk){
$db_lnk = $this->_rw_lnk;
} else if($this->_ro_lnk){
$db_lnk = $this->_ro_lnk;
} else {
return 0;
}
return mysqli_errno($db_lnk);
}
public function beginTransaction(){
if(!$this->_in_transaction){
$this->_in_transaction = true;
if(!$this->_use_transaction){
$this->_use_transaction = true;
if(isset($this->_ro_lnk)){
$this->_ro_conn();
}
}
return $this->exec('START TRANSACTION');
} else {
return false;
}
}
public function commit($status=true){
if($status){
$this->exec('COMMIT');
$this->_in_transaction = false;
return true;
}
return false;
}
public function rollBack(){
$this->exec('ROLLBACK');
$this->_in_transaction = false;
}
public function close(){
if($this->_rw_lnk && mysqli_close($this->_rw_lnk)){
$this->_rw_lnk = null;
return true;
}
return false;
}
public function ping(){
if($this->_rw_lnk){
return mysqli_ping($this->_rw_lnk);
}
return false;
}
}