123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- <?php
- namespace Mall\Framework\Db;
- class Db
- {
- static $queries = 0;
- public $master = array(), $slaves = array(), $slave = array(), $slave_key, $sql, $error, $errno;
- private $dbh, $dbh_master, $dbh_slave, $foreign_key_checks = 0;
- public function __construct($master = array(), $slaves = array())
- {
- $this->master = $master;
- $this->dbh_master = $this->connect($master);
- $this->dbh = $this->dbh_master;
- if(!empty($slaves)){
- $this->slaves = $slaves;
- $this->dbh_slave = $this->connect_slave();
- $this->dbh = $this->dbh_slave;
- }
- }
- public function __call($method, $args)
- {
- if (in_array($method, array('errorCode', 'errorInfo', 'getAttribute', 'lastInsertId', 'quote', 'setAttribute'), true)) {
- if (in_array($method, array('lastInsertId'))) {
- return $this->dbh_master()->$method();
- }
- if (isset($args[0])) {
- return isset($args[1]) ? $this->dbh()->$method($args[0], $args[1]) : $this->dbh()->$method($args[0]);
- } else {
- return $this->dbh()->$method();
- }
- }
- }
- public function beginTransaction($foreign_key_checks = false)
- {
- try{
- $result = $this->dbh_master()->beginTransaction();
- if ($result && !$foreign_key_checks) {
- $this->foreign_key_checks = intval($this->dbh_master()->query("SELECT @@FOREIGN_KEY_CHECKS")->fetchColumn(0));
- $this->foreign_key_checks && $this->dbh_master()->query("SET @@FOREIGN_KEY_CHECKS=0");
- }
- return $result;
- }catch (\PDOException $e){
- if ( isset($e->errorInfo) && ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) ) {
- $this->closeConnection();
- return $this->beginTransaction($foreign_key_checks);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function commit()
- {
- try {
- $result = $this->dbh_master()->commit();
- if ($this->foreign_key_checks) {
- $this->dbh_master()->query("SET @@FOREIGN_KEY_CHECKS=1");
- $this->foreign_key_checks = 0;
- }
- return $result;
- }catch (\PDOException $e){
- if ( isset($e->errorInfo) && ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) ) {
- $this->closeConnection();
- return $this->commit();
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function rollBack()
- {
- try{
- $result = $this->dbh_master()->rollBack();
- if ($this->foreign_key_checks) {
- $this->dbh_master()->query("SET @@FOREIGN_KEY_CHECKS=1");
- $this->foreign_key_checks = 0;
- }
- return $result;
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->rollBack();
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function connect($options = array())
- {
- try {
- $dbh = new \PDO($options['driver'] . ':host=' . $options['host'] . ';port=' . $options['port'] . ';dbname=' . $options['dbname'] . ';charset=' . $options['charset'], $options['username'], $options['password'], array(
- \PDO::ATTR_PERSISTENT => ($options['pconnect'] ? true : false),)
- );
- $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
- $dbh->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, false);
- $dbh->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
- } catch (\PDOException $e) {
- $this->errno = $e->getCode();
- $this->error = $e->getMessage();
- return false;
- }
- if ($options['driver'] == 'mysql') {
- $dbh->exec("SET character_set_connection='" . $options['charset'] . "',character_set_results='" . $options['charset'] . "',character_set_client='{$options['charset']}'" . ($dbh->query("SELECT version()")->fetchColumn(0) > '5.0.1' ? ",sql_mode=''" : ''));
- }
- return $dbh;
- }
- private function connect_slave()
- {
- $this->slave_key = array_rand($this->slaves);
- $this->slave = $this->slaves[$this->slave_key];
- $this->dbh_slave = $this->connect($this->slave);
- if (!$this->dbh_slave && count($this->slaves) > 1) {
- unset($this->slaves[$this->slave_key]);
- return $this->connect_slave();
- }
- return $this->dbh_slave;
- }
- // https://github.com/walkor/mysql/blob/master/src/Connection.php
- // https://my.oschina.net/u/222608/blog/1621402
- public function closeConnection(){
- $this->dbh = null;
- $this->dbh_master = null;
- $this->dbh_slave = null;
- }
- public function exec($statement)
- {
- try{
- return $this->dbh($statement) ? $this->dbh->exec($this->sql) : false;
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->exec($statement);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$this->sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function prepare($statement, $driver_options = array())
- {
- try{
- return $this->dbh($statement) ? $this->dbh->prepare($this->sql, $driver_options) : false;
- }catch (\PDOException $e){
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'prepare'.var_export('prepare超时错误了',true).PHP_EOL,FILE_APPEND);
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->prepare($statement, $driver_options);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$this->sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'prepare'.var_export($e->getMessage(),true).PHP_EOL,FILE_APPEND);
- return false;
- }
- }
- }
- public function exportQuery($statement)
- {
- try {
- if($this->dbh($statement)){
- $this->dbh->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
- return $this->dbh->query($this->sql,\PDO::FETCH_ASSOC)->fetchAll();
- }else{
- return false;
- }
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->query($statement);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$this->sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function query($statement)
- {
- try {
- return $this->dbh($statement) ? $this->dbh->query($this->sql,\PDO::FETCH_ASSOC)->fetchAll() : false;
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->query($statement);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$this->sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- private function dbh($sql = null)
- {
- if($sql === null){
- $this->sql = null;
- }else{
- $this->sql = $sql;
- }
- if (is_null($this->dbh)) {
- if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
- $this->dbh = $this->dbh_master;
- }
- return $this->dbh;
- self::$queries++;
- $this->sql = str_replace('#table_', $this->master['prefix'], trim($sql));
- if ($this->slaves && is_null($this->dbh_master) && stripos($this->sql, 'select') === 0) {
- if (is_null($this->dbh_slave)) $this->dbh_slave = $this->connect_slave();
- $this->dbh = $this->dbh_slave;
- } else {
- if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
- $this->dbh = $this->dbh_master;
- }
- return $this->dbh;
- }
- public function get($sql, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
- {
- $this->sql = $sql;
- try{
- $db = $this->prepare($sql);
- if (!$db) return false;
- if ($db->execute($data)) {
- return $db->fetch($fetch_style);
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }catch (\PDOException $e){
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'get'.var_export('get超时错误了',true).PHP_EOL,FILE_APPEND);
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->get($sql, $data, $fetch_style);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function cursor($sth)
- {
- while($row = $sth->fetch(\PDO::FETCH_ASSOC)) {
- yield $row;
- }
- }
- public function exportSelect($sql, $data = array())
- {
- $this->sql = $sql;
- try{
- $db = $this->prepare($sql);
- if (!$db) return false;
- $this->dbh->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
- if ($db->execute($data)) {
- return $this->cursor($db);
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->exportSelect($sql, $data);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$sql." ".$msg;
- echo $err_msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function select($sql, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
- {
- $this->sql = $sql;
- try{
- //V($sql);
- $db = $this->prepare($sql);
- if (!$db) return false;
- if ($db->execute($data)) {
- return $db->fetchAll($fetch_style);
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }catch (\PDOException $e){
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'select'.var_export('select超时错误了',true).PHP_EOL,FILE_APPEND);
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->select($sql, $data, $fetch_style);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function insert($sql, $data = array(), $multiple = false)
- {
- $this->sql = $sql;
- try{
- $db = $this->prepare($sql);
- if (!$db) return false;
- if (empty($data)) {
- if ($db->execute()) {
- $insertid = $this->dbh_master->lastInsertId();
- return $insertid ? $insertid : true;
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }
- if ($multiple) {
- $insertids = [];
- foreach ($data as $r) {
- $this->_bindValue($db, $r);
- if ($db->execute()) {
- $insertids[] = $this->dbh_master->lastInsertId();
- }else{
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }
- return !empty($insertids) ? $insertids : true;
- } else {
- $this->_bindValue($db, $data);
- if ($db->execute()) {
- $insertid = $this->dbh_master->lastInsertId();
- return $insertid > 0 ? $insertid : true;
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->insert($sql, $data, $multiple);
- }else{
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'insert'.var_export($data,true).PHP_EOL,FILE_APPEND);
- $msg = $e->getMessage();
- $err_msg = "SQL:".$sql." ".$msg;
- $this->sql = $sql;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'insert'.var_export($e->getMessage(),true).PHP_EOL,FILE_APPEND);
- return false;
- }
- }
- }
- public function update($sql, $data = array(), $multiple = false)
- {
- $this->sql = $sql;
- try{
- $db = $this->prepare($sql);
- if (!$db) return false;
- if (empty($data)) {
- if ($db->execute()) {
- $rowcount = $db->rowCount();
- return $rowcount ? $rowcount : true;
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }
- if ($multiple) {
- foreach ($data as $r) {
- $this->_bindValue($db, $r);
- if (!$db->execute()) {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }
- return true;
- } else {
- $this->_bindValue($db, $data);
- if ($db->execute()) {
- $rowcount = $db->rowCount();
- return $rowcount ? $rowcount : true;
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->update($sql, $data, $multiple);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function replace($sql, $data = array(), $multiple = false)
- {
- return $this->update($sql, $data, $multiple);
- }
- public function delete($sql, $data = array())
- {
- $this->sql = $sql;
- try{
- $db = $this->prepare($sql);
- if (!$db) return false;
- if ($db->execute($data)) {
- $rowcount = $db->rowCount();
- return $rowcount ? $rowcount : true;
- } else {
- $this->errno = $db->errorCode();
- $this->error = $db->errorInfo();
- return false;
- }
- }catch (\PDOException $e){
- if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) {
- $this->closeConnection();
- return $this->delete($sql, $data);
- }else{
- $msg = $e->getMessage();
- $err_msg = "SQL:".$sql." ".$msg;
- $this->errno = (int)$e->getCode();
- $this->error = $err_msg;
- return false;
- }
- }
- }
- public function limit($sql, $limit = 0, $offset = 0, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
- {
- if ($limit > 0) $sql .= $offset > 0 ? " LIMIT $offset, $limit" : " LIMIT $limit";
- return $this->select($sql, $data, $fetch_style);
- }
- public function page($sql, $page = 1, $size = 20, $data = array(), $fetch_style = \PDO::FETCH_ASSOC)
- {
- $page = isset($page) ? max(intval($page), 1) : 1;
- $size = max(intval($size), 1);
- $offset = ($page - 1) * $size;
- return $this->limit($sql, $size, $offset, $data, $fetch_style);
- }
- public function select_db($dbname)
- {
- return $this->exec("USE $dbname");
- }
- public function list_fields($table, $field = null)
- {
- $sql = "SHOW COLUMNS FROM `$table`";
- if ($field) $sql .= " LIKE '$field'";
- return $this->query($sql);
- }
- public function list_tables($dbname = null)
- {
- $tables = array();
- $sql = $dbname ? "SHOW TABLES FROM `$dbname`" : "SHOW TABLES";
- $result = $this->query($sql);
- foreach ($result as $r) {
- $tables[] = array_pop($r);
- }
- return $tables;
- }
- public function list_dbs()
- {
- $dbs = array();
- $result = $this->query("SHOW DATABASES");
- foreach ($result as $r) {
- foreach ($r as $db) $dbs[] = $db;
- }
- return $dbs;
- }
- public function get_primary($table)
- {
- $primary = array();
- $result = $this->query("SHOW COLUMNS FROM `$table`");
- foreach ($result as $r) {
- if ($r['Key'] == 'PRI') $primary[] = $r['Field'];
- }
- return count($primary) == 1 ? $primary[0] : (empty($primary) ? null : $primary);
- }
- public function get_var($var = null)
- {
- $variables = array();
- $sql = is_null($var) ? '' : " LIKE '$var'";
- $result = $this->query("SHOW VARIABLES $sql");
- foreach ($result as $r) {
- if (!is_null($var) && isset($r['Value'])) return $r['Value'];
- $variables[$r['Variable_name']] = $r['Value'];
- }
- return $variables;
- }
- public function version()
- {
- $db = $this->query("SELECT version()");
- return $db ? $db->fetchColumn(0) : false;
- }
- public function prefix()
- {
- return $this->master['prefix'];
- }
- public function errno()
- {
- return is_null($this->errno) ? $this->errorCode() : $this->errno;
- }
- public function error()
- {
- if (is_null($this->error)) {
- return $this->errorInfo();
- } else {
- /*if($this->sql && $this->error){
- $this->error .= 'sql:'.$this->sql;
- }*/
- return $this->error;
- }
- }
- /**
- * 检查数据库连接,是否有效,无效则重新建立
- */
- protected function checkConnection()
- {
- if (!$this->pdo_ping())
- {
- $this->dbh_master = NULL;
- }
- }
- /**
- * 检查连接是否可用
- * @return Boolean
- */
- function pdo_ping(){
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'ping'.var_export(is_null($this->dbh),true).PHP_EOL,FILE_APPEND);
- if( is_null($this->dbh) ){
- return false;
- }
- if (!$this->dbh->getAttribute(\PDO::ATTR_SERVER_INFO)) {
- $this->errno = $this->dbh->errorCode();
- $this->error = $this->dbh->errorInfo();
- file_put_contents('/tmp/transaction.log',date('Y-m-d H:i:s').'error'.var_export($this->errno.':'.$this->error,true).PHP_EOL,FILE_APPEND);
- if($this->errno == 'HY000'){
- $this->dbh_master = null;
- }
- return false;
- }
- return true;
- }
- private function dbh_master()
- {
- //$this->checkConnection();
- if (is_null($this->dbh_master)) $this->dbh_master = $this->connect($this->master);
- $this->dbh = $this->dbh_master;
- return $this->dbh;
- }
- private function _bindValue(& $db, $data)
- {
- if (!is_array($data)) return false;
- foreach ($data as $k => $v) {
- $k = is_numeric($k) ? $k + 1 : ':' . $k;
- $db->bindValue($k, $v);
- }
- return true;
- }
- }
|