// +---------------------------------------------------------------------- //---------------------------------- // PHPSpider数据库类文件 //---------------------------------- namespace phpspider\core; class db { private static $configs = array(); private static $rsid; private static $links = array(); private static $link_name = 'default'; private static $autocommiting = false; public static function _init() { // 获取配置 $config = self::$link_name == 'default' ? self::_get_default_config() : self::$configs[self::$link_name]; // 创建连接 if (empty(self::$links[self::$link_name]) || empty(self::$links[self::$link_name]['conn'])) { // 第一次连接,初始化fail和pid if (empty(self::$links[self::$link_name])) { self::$links[self::$link_name]['fail'] = 0; self::$links[self::$link_name]['pid'] = function_exists('posix_getpid') ? posix_getpid() : 0; //echo "progress[".self::$links[self::$link_name]['pid']."] create db connect[".self::$link_name."]\n"; } self::$links[self::$link_name]['conn'] = mysqli_connect($config['host'], $config['user'], $config['pass'], $config['name'], $config['port']); if(mysqli_connect_errno()) { self::$links[self::$link_name]['fail']++; $errmsg = 'Mysql Connect failed['.self::$links[self::$link_name]['fail'].']: ' . mysqli_connect_error(); echo util::colorize(date("H:i:s") . " {$errmsg}\n\n", 'fail'); log::add($errmsg, "Error"); // 连接失败5次,中断进程 if (self::$links[self::$link_name]['fail'] >= 5) { exit(250); } self::_init($config); } else { mysqli_query(self::$links[self::$link_name]['conn'], " SET character_set_connection=utf8, character_set_results=utf8, character_set_client=binary, sql_mode='' "); } } else { $curr_pid = function_exists('posix_getpid') ? posix_getpid() : 0; // 如果父进程已经生成资源就释放重新生成,因为多进程不能共享连接资源 if (self::$links[self::$link_name]['pid'] != $curr_pid) { self::clear_link(); } } } /** * 重新设置连接 * 传空的话就等于关闭数据库再连接 * 在多进程环境下如果主进程已经调用过了,子进程一定要调用一次 clear_link,否则会报错: * Error while reading greeting packet. PID=19615,这是两个进程互抢一个连接句柄引起的 * * @param array $config * @return void * @author seatle * @created time :2016-03-29 00:51 */ public static function clear_link() { if(self::$links) { foreach(self::$links as $k=>$v) { @mysqli_close($v['conn']); unset(self::$links[$k]); } } // 注意,只会连接最后一个,不过貌似也够用了啊 self::_init(); } /** * 改变链接为指定配置的链接(如果不同时使用多个数据库,不会涉及这个操作) * @parem $link_name 链接标识名 * @parem $config 多次使用时, 这个数组只需传递一次 * config 格式与 $GLOBALS['config']['db'] 一致 * @return void */ public static function set_connect($link_name, $config = array()) { self::$link_name = $link_name; if (!empty($config)) { self::$configs[self::$link_name] = $config; } else { if (empty(self::$configs[self::$link_name])) { throw new Exception("You not set a config array for connect!"); } } } /** * 还原为默认连接(如果不同时使用多个数据库,不会涉及这个操作) * @parem $config 指定配置(默认使用inc_config.php的配置) * @return void */ public static function set_connect_default() { $config = self::_get_default_config(); self::set_connect('default', $config); } /** * 获取默认配置 */ protected static function _get_default_config() { if (empty(self::$configs['default'])) { if (!is_array($GLOBALS['config']['db'])) { exit('db.php _get_default_config()' . '没有mysql配置'); } self::$configs['default'] = $GLOBALS['config']['db']; } return self::$configs['default']; } /** * 返回查询游标 * @return rsid */ protected static function _get_rsid($rsid = '') { return $rsid == '' ? self::$rsid : $rsid; } public static function autocommit($mode = false) { if ( self::$autocommiting ) { return true; } self::$autocommiting = true; self::_init(); return mysqli_autocommit(self::$links[self::$link_name]['conn'], $mode); } public static function begin_tran() { return self::autocommit(false); } public static function commit() { mysqli_commit(self::$links[self::$link_name]['conn']); self::autocommit(true); return true; } public static function rollback() { mysqli_rollback(self::$links[self::$link_name]['conn']); self::autocommit(true); return true; } public static function query($sql) { $sql = trim($sql); // 初始化数据库 self::_init(); self::$rsid = @mysqli_query(self::$links[self::$link_name]['conn'], $sql); if (self::$rsid === false) { // 不要每次都ping,浪费流量浪费性能,执行出错了才重新连接 $errno = mysqli_errno(self::$links[self::$link_name]['conn']); if ($errno == 2013 || $errno == 2006) { $errmsg = mysqli_error(self::$links[self::$link_name]['conn']); log::add($errmsg, "Error"); @mysqli_close(self::$links[self::$link_name]['conn']); self::$links[self::$link_name]['conn'] = null; return self::query($sql); } $errmsg = "Query SQL: ".$sql; log::add($errmsg, "Warning"); $errmsg = "Error SQL: ".mysqli_error(self::$links[self::$link_name]['conn']); log::add($errmsg, "Warning"); $backtrace = debug_backtrace(); array_shift($backtrace); $narr = array('class', 'type', 'function', 'file', 'line'); $err = "debug_backtrace:\n"; foreach($backtrace as $i => $l) { foreach($narr as $k) { if( !isset($l[$k]) ) { $l[$k] = ''; } } $err .= "[$i] in function {$l['class']}{$l['type']}{$l['function']} "; if($l['file']) $err .= " in {$l['file']} "; if($l['line']) $err .= " on line {$l['line']} "; $err .= "\n"; } log::add($err); return false; } else { return self::$rsid; } } public static function fetch($rsid = '') { $rsid = self::_get_rsid($rsid); $row = mysqli_fetch_array($rsid, MYSQLI_ASSOC); return $row; } public static function get_one($sql) { if (!preg_match("/limit/i", $sql)) { $sql = preg_replace("/[,;]$/i", '', trim($sql)) . " limit 1 "; } $rsid = self::query($sql); if ($rsid === false) { return array(); } $row = self::fetch($rsid); self::free($rsid); return $row; } public static function get_all($sql) { $rsid = self::query($sql); if ($rsid === false) { return array(); } while ( $row = self::fetch($rsid) ) { $rows[] = $row; } self::free($rsid); return empty($rows) ? false : $rows; } public static function free($rsid) { return mysqli_free_result($rsid); } public static function insert_id() { return mysqli_insert_id(self::$links[self::$link_name]['conn']); } public static function affected_rows() { return mysqli_affected_rows(self::$links[self::$link_name]['conn']); } public static function insert($table = '', $data = null, $return_sql = false) { $items_sql = $values_sql = ""; foreach ($data as $k => $v) { $v = stripslashes($v); $v = addslashes($v); $items_sql .= "`$k`,"; $values_sql .= "\"$v\","; } $sql = "Insert Ignore Into `{$table}` (" . substr($items_sql, 0, -1) . ") Values (" . substr($values_sql, 0, -1) . ")"; if ($return_sql) { return $sql; } else { if (self::query($sql)) { return mysqli_insert_id(self::$links[self::$link_name]['conn']); } else { return false; } } } public static function insert_batch($table = '', $set = NULL, $return_sql = FALSE) { if (empty($table) || empty($set)) { return false; } $set = self::strsafe($set); $fields = self::get_fields($table); $keys_sql = $vals_sql = array(); foreach ($set as $i=>$val) { ksort($val); $vals = array(); foreach ($val as $k => $v) { // 过滤掉数据库没有的字段 if (!in_array($k, $fields)) { continue; } // 如果是第一个数组,把key当做插入条件 if ($i == 0 && $k == 0) { $keys_sql[] = "`$k`"; } $vals[] = "\"$v\""; } $vals_sql[] = implode(",", $vals); } $sql = "Insert Ignore Into `{$table}`(".implode(", ", $keys_sql).") Values (".implode("), (", $vals_sql).")"; if ($return_sql) return $sql; $rt = self::query($sql); $insert_id = self::insert_id(); $return = empty($insert_id) ? $rt : $insert_id; return $return; } public static function update_batch($table = '', $set = NULL, $index = NULL, $where = NULL, $return_sql = FALSE) { if (empty($table) || is_null($set) || is_null($index)) { // 不要用exit,会中断程序 return false; } $set = self::strsafe($set); $fields = self::get_fields($table); $ids = array(); foreach ($set as $val) { ksort($val); // 去重,其实不去也可以,因为相同的when只会执行第一个,后面的就直接跳过不执行了 $key = md5($val[$index]); $ids[$key] = $val[$index]; foreach (array_keys($val) as $field) { if ($field != $index) { $final[$field][$key] = 'When `'.$index.'` = "'.$val[$index].'" Then "'.$val[$field].'"'; } } } //$ids = array_values($ids); // 如果不是数组而且不为空,就转数组 if (!is_array($where) && !empty($where)) { $where = array($where); } $where[] = $index.' In ("'.implode('","', $ids).'")'; $where = empty($where) ? "" : " Where ".implode(" And ", $where); $sql = "Update `".$table."` Set "; $cases = ''; foreach ($final as $k => $v) { // 过滤掉数据库没有的字段 if (!in_array($k, $fields)) { continue; } $cases .= '`'.$k.'` = Case '."\n"; foreach ($v as $row) { $cases .= $row."\n"; } $cases .= 'Else `'.$k.'` End, '; } $sql .= substr($cases, 0, -2); // 其实不带 Where In ($index) 的条件也可以的 $sql .= $where; if ($return_sql) return $sql; $rt = self::query($sql); $insert_id = self::affected_rows(); $return = empty($affected_rows) ? $rt : $affected_rows; return $return; } public static function update($table = '', $data = array(), $where = null, $return_sql = false) { $sql = "UPDATE `{$table}` SET "; foreach ($data as $k => $v) { $v = stripslashes($v); $v = addslashes($v); $sql .= "`{$k}` = \"{$v}\","; } if (!is_array($where)) { $where = array($where); } // 删除空字段,不然array("")会成为WHERE foreach ($where as $k => $v) { if (empty($v)) { unset($where[$k]); } } $where = empty($where) ? "" : " Where " . implode(" And ", $where); $sql = substr($sql, 0, -1) . $where; if ($return_sql) { return $sql; } else { if (self::query($sql)) { return mysqli_affected_rows(self::$links[self::$link_name]['conn']); } else { return false; } } } public static function delete($table = '', $where = null, $return_sql = false) { // 小心全部被删除了 if (empty($where)) { return false; } $where = 'Where ' . (!is_array($where) ? $where : implode(' And ', $where)); $sql = "Delete From `{$table}` {$where}"; if ($return_sql) { return $sql; } else { if (self::query($sql)) { return mysqli_affected_rows(self::$links[self::$link_name]['conn']); } else { return false; } } } public static function ping() { if (!mysqli_ping(self::$links[self::$link_name]['conn'])) { @mysqli_close(self::$links[self::$link_name]['conn']); self::$links[self::$link_name]['conn'] = null; self::_init(); } } public static function strsafe($array) { $arrays = array(); if(is_array($array)===true) { foreach ($array as $key => $val) { if(is_array($val)===true) { $arrays[$key] = self::strsafe($val); } else { //先去掉转义,避免下面重复转义了 $val = stripslashes($val); //进行转义 $val = addslashes($val); //处理addslashes没法处理的 _ % 字符 //$val = strtr($val, array('_'=>'\_', '%'=>'\%')); $arrays[$key] = $val; } } return $arrays; } else { $array = stripslashes($array); $array = addslashes($array); //$array = strtr($array, array('_'=>'\_', '%'=>'\%')); return $array; } } // 这个是给insert、update、insert_batch、update_batch用的 public static function get_fields($table) { // $sql = "SHOW COLUMNS FROM $table"; //和下面的语句效果一样 $rows = self::get_all("Desc `{$table}`"); $fields = array(); foreach ($rows as $k => $v) { // 过滤自增主键 // if ($v['Key'] != 'PRI') if ($v['Extra'] != 'auto_increment') { $fields[] = $v['Field']; } } return $fields; } public static function table_exists($table_name) { $sql = "SHOW TABLES LIKE '" . $table_name . "'"; $rsid = self::query($sql); $table = self::fetch($rsid); if (empty($table)) { return false; } return true; } }