db.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <?php
  2. class Db {
  3. protected static $_dbh = null; //静态属性,所有数据库实例共用,避免重复连接数据库
  4. protected $_dbType = 'mysql';
  5. protected $_pconnect = false; //是否使用长连接
  6. protected $_host = 'localhost';
  7. protected $_port = 3306;
  8. protected $_user = 'root';
  9. protected $_pass = '123456';
  10. protected $_dbName = 'fangweima'; //数据库名
  11. protected $_sql = false; //最后一条sql语句
  12. protected $_where = '';
  13. protected $_order = '';
  14. protected $_limit = '';
  15. protected $_field = '*';
  16. protected $_clear = 0; //状态,0表示查询条件干净,1表示查询条件污染
  17. protected $_trans = 0; //事务指令数
  18. /**
  19. * 初始化类
  20. * @param array $conf 数据库配置
  21. */
  22. public function __construct(array $conf) {
  23. class_exists('PDO') or die("PDO: class not exists.");
  24. $this->_host = $conf['host'];
  25. $this->_port = $conf['port'];
  26. $this->_user = $conf['user'];
  27. $this->_pass = $conf['passwd'];
  28. $this->_dbName = $conf['dbname'];
  29. //连接数据库
  30. if ( is_null(self::$_dbh) ) {
  31. $this->_connect();
  32. }
  33. }
  34. /**
  35. * 连接数据库的方法
  36. */
  37. protected function _connect() {
  38. $dsn = $this->_dbType.':host='.$this->_host.';port='.$this->_port.';dbname='.$this->_dbName;
  39. $options = $this->_pconnect ? array(PDO::ATTR_PERSISTENT=>true) : array();
  40. try {
  41. $dbh = new PDO($dsn, $this->_user, $this->_pass, $options);
  42. $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //设置如果sql语句执行错误则抛出异常,事务会自动回滚
  43. $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果(防SQL注入)
  44. } catch (PDOException $e) {
  45. die('Connection failed: ' . $e->getMessage());
  46. }
  47. $dbh->exec('SET NAMES utf8');
  48. self::$_dbh = $dbh;
  49. }
  50. /**
  51. * 字段和表名添加 `符号
  52. * 保证指令中使用关键字不出错 针对mysql
  53. * @param string $value
  54. * @return string
  55. */
  56. protected function _addChar($value) {
  57. if ('*'==$value || false!==strpos($value,'(') || false!==strpos($value,'.') || false!==strpos($value,'`')) {
  58. //如果包含* 或者 使用了sql方法 则不作处理
  59. } elseif (false === strpos($value,'`') ) {
  60. $value = '`'.trim($value).'`';
  61. }
  62. return $value;
  63. }
  64. /**
  65. * 取得数据表的字段信息
  66. * @param string $tbName 表名
  67. * @return array
  68. */
  69. protected function _tbFields($tbName) {
  70. $sql = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME="'.$tbName.'" AND TABLE_SCHEMA="'.$this->_dbName.'"';
  71. $stmt = self::$_dbh->prepare($sql);
  72. $stmt->execute();
  73. $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
  74. $ret = array();
  75. foreach ($result as $key=>$value) {
  76. $ret[$value['COLUMN_NAME']] = 1;
  77. }
  78. return $ret;
  79. }
  80. /**
  81. * 过滤并格式化数据表字段
  82. * @param string $tbName 数据表名
  83. * @param array $data POST提交数据
  84. * @return array $newdata
  85. */
  86. protected function _dataFormat($tbName,$data) {
  87. if (!is_array($data)) return array();
  88. $table_column = $this->_tbFields($tbName);
  89. $ret=array();
  90. foreach ($data as $key=>$val) {
  91. if (!is_scalar($val)) continue; //值不是标量则跳过
  92. if (array_key_exists($key,$table_column)) {
  93. $key = $this->_addChar($key);
  94. if (is_int($val)) {
  95. $val = intval($val);
  96. } elseif (is_float($val)) {
  97. $val = floatval($val);
  98. } elseif (preg_match('/^\(\w*(\+|\-|\*|\/)?\w*\)$/i', $val)) {
  99. // 支持在字段的值里面直接使用其它字段 ,例如 (score+1) (name) 必须包含括号
  100. $val = $val;
  101. } elseif (is_string($val)) {
  102. $val = '"'.addslashes($val).'"';
  103. }
  104. $ret[$key] = $val;
  105. }
  106. }
  107. return $ret;
  108. }
  109. /**
  110. * 执行查询 主要针对 SELECT, SHOW 等指令
  111. * @param string $sql sql指令
  112. * @return mixed
  113. */
  114. protected function _doQuery($sql='') {
  115. $this->_sql = $sql;
  116. $pdostmt = self::$_dbh->prepare($this->_sql); //prepare或者query 返回一个PDOStatement
  117. $pdostmt->execute();
  118. $result = $pdostmt->fetchAll(PDO::FETCH_ASSOC);
  119. return $result;
  120. }
  121. /**
  122. * 执行语句 针对 INSERT, UPDATE 以及DELETE,exec结果返回受影响的行数
  123. * @param string $sql sql指令
  124. * @return integer
  125. */
  126. protected function _doExec($sql='') {
  127. $this->_sql = $sql;
  128. return self::$_dbh->exec($this->_sql);
  129. }
  130. /**
  131. * 执行sql语句,自动判断进行查询或者执行操作
  132. * @param string $sql SQL指令
  133. * @return mixed
  134. */
  135. public function doSql($sql='') {
  136. $queryIps = 'INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|LOAD DATA|SELECT .* INTO|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK';
  137. if (preg_match('/^\s*"?(' . $queryIps . ')\s+/i', $sql)) {
  138. return $this->_doExec($sql);
  139. }
  140. else {
  141. //查询操作
  142. return $this->_doQuery($sql);
  143. }
  144. }
  145. /**
  146. * 获取最近一次查询的sql语句
  147. * @return String 执行的SQL
  148. */
  149. public function getLastSql() {
  150. return $this->_sql;
  151. }
  152. /**
  153. * 插入方法
  154. * @param string $tbName 操作的数据表名
  155. * @param array $data 字段-值的一维数组
  156. * @return int 受影响的行数
  157. */
  158. public function insert($tbName,array $data){
  159. $data = $this->_dataFormat($tbName,$data);
  160. if (!$data) return;
  161. $sql = "insert into ".$tbName."(".implode(',',array_keys($data)).") values(".implode(',',array_values($data)).")";
  162. return $this->_doExec($sql);
  163. }
  164. /**
  165. * 删除方法
  166. * @param string $tbName 操作的数据表名
  167. * @return int 受影响的行数
  168. */
  169. public function delete($tbName) {
  170. //安全考虑,阻止全表删除
  171. if (!trim($this->_where)) return false;
  172. $sql = "delete from ".$tbName." ".$this->_where;
  173. $this->_clear = 1;
  174. $this->_clear();
  175. return $this->_doExec($sql);
  176. }
  177. /**
  178. * 更新函数
  179. * @param string $tbName 操作的数据表名
  180. * @param array $data 参数数组
  181. * @return int 受影响的行数
  182. */
  183. public function update($tbName,array $data) {
  184. //安全考虑,阻止全表更新
  185. if (!trim($this->_where)) return false;
  186. $data = $this->_dataFormat($tbName,$data);
  187. if (!$data) return;
  188. $valArr = '';
  189. foreach($data as $k=>$v){
  190. $valArr[] = $k.'='.$v;
  191. }
  192. $valStr = implode(',', $valArr);
  193. $sql = "update ".trim($tbName)." set ".trim($valStr)." ".trim($this->_where);
  194. return $this->_doExec($sql);
  195. }
  196. /**
  197. * 查询函数
  198. * @param string $tbName 操作的数据表名
  199. * @return array 结果集
  200. */
  201. public function select($tbName='') {
  202. $sql = "select ".trim($this->_field)." from ".$tbName." ".trim($this->_where)." ".trim($this->_order)." ".trim($this->_limit);
  203. $this->_clear = 1;
  204. $this->_clear();
  205. return $this->_doQuery(trim($sql));
  206. }
  207. /**
  208. * @param mixed $option 组合条件的二维数组,例:$option['field1'] = array(1,'=>','or')
  209. * @return $this
  210. */
  211. public function where($option) {
  212. if ($this->_clear>0) $this->_clear();
  213. $this->_where = ' where ';
  214. $logic = 'and';
  215. if (is_string($option)) {
  216. $this->_where .= $option;
  217. }
  218. elseif (is_array($option)) {
  219. foreach($option as $k=>$v) {
  220. if (is_array($v)) {
  221. $relative = isset($v[1]) ? $v[1] : '=';
  222. $logic = isset($v[2]) ? $v[2] : 'and';
  223. $condition = ' ('.$this->_addChar($k).' '.$relative.' '.$v[0].') ';
  224. }
  225. else {
  226. $logic = 'and';
  227. $condition = ' ('.$this->_addChar($k).'='.$v.') ';
  228. }
  229. $this->_where .= isset($mark) ? $logic.$condition : $condition;
  230. $mark = 1;
  231. }
  232. }
  233. return $this;
  234. }
  235. /**
  236. * 设置排序
  237. * @param mixed $option 排序条件数组 例:array('sort'=>'desc')
  238. * @return $this
  239. */
  240. public function order($option) {
  241. if ($this->_clear>0) $this->_clear();
  242. $this->_order = ' order by ';
  243. if (is_string($option)) {
  244. $this->_order .= $option;
  245. }
  246. elseif (is_array($option)) {
  247. foreach($option as $k=>$v){
  248. $order = $this->_addChar($k).' '.$v;
  249. $this->_order .= isset($mark) ? ','.$order : $order;
  250. $mark = 1;
  251. }
  252. }
  253. return $this;
  254. }
  255. /**
  256. * 设置查询行数及页数
  257. * @param int $page pageSize不为空时为页数,否则为行数
  258. * @param int $pageSize 为空则函数设定取出行数,不为空则设定取出行数及页数
  259. * @return $this
  260. */
  261. public function limit($page,$pageSize=null) {
  262. if ($this->_clear>0) $this->_clear();
  263. if ($pageSize===null) {
  264. $this->_limit = "limit ".$page;
  265. }
  266. else {
  267. $pageval = intval( ($page - 1) * $pageSize);
  268. $this->_limit = "limit ".$pageval.",".$pageSize;
  269. }
  270. return $this;
  271. }
  272. /**
  273. * 设置查询字段
  274. * @param mixed $field 字段数组
  275. * @return $this
  276. */
  277. public function field($field){
  278. if ($this->_clear>0) $this->_clear();
  279. if (is_string($field)) {
  280. $field = explode(',', $field);
  281. }
  282. $nField = array_map(array($this,'_addChar'), $field);
  283. $this->_field = implode(',', $nField);
  284. return $this;
  285. }
  286. /**
  287. * 清理标记函数
  288. */
  289. protected function _clear() {
  290. $this->_where = '';
  291. $this->_order = '';
  292. $this->_limit = '';
  293. $this->_field = '*';
  294. $this->_clear = 0;
  295. }
  296. /**
  297. * 手动清理标记
  298. * @return $this
  299. */
  300. public function clearKey() {
  301. $this->_clear();
  302. return $this;
  303. }
  304. /**
  305. * 启动事务
  306. * @return void
  307. */
  308. public function startTrans() {
  309. //数据rollback 支持
  310. if ($this->_trans==0) self::$_dbh->beginTransaction();
  311. $this->_trans++;
  312. return;
  313. }
  314. /**
  315. * 用于非自动提交状态下面的查询提交
  316. * @return boolen
  317. */
  318. public function commit() {
  319. $result = true;
  320. if ($this->_trans>0) {
  321. $result = self::$_dbh->commit();
  322. $this->_trans = 0;
  323. }
  324. return $result;
  325. }
  326. /**
  327. * 事务回滚
  328. * @return boolen
  329. */
  330. public function rollback() {
  331. $result = true;
  332. if ($this->_trans>0) {
  333. $result = self::$_dbh->rollback();
  334. $this->_trans = 0;
  335. }
  336. return $result;
  337. }
  338. /**
  339. * 关闭连接
  340. * PHP 在脚本结束时会自动关闭连接。
  341. */
  342. public function close() {
  343. if (!is_null(self::$_dbh)) self::$_dbh = null;
  344. }
  345. }
  346. /*
  347. $mysql = new Db($config);
  348. //插入
  349. $data = array(
  350. 'sid'=>101,
  351. 'aa'=>123456,
  352. 'bbc'=>'aaaaaaaaaaaaaa',
  353. );
  354. $mysql->insert('t_table',$data);
  355. //查询
  356. $res = $mysql->field(array('sid','aa','bbc'))
  357. ->order(array('sid'=>'desc','aa'=>'asc'))
  358. ->where(array('sid'=>"101",'aa'=>array('123455','>','or')))
  359. ->limit(1,2)
  360. ->select('t_table');
  361. $res = $mysql->field('sid,aa,bbc')
  362. ->order('sid desc,aa asc')
  363. ->where('sid=101 or aa>123455')
  364. ->limit(1,2)
  365. ->select('t_table');
  366. //获取最后执行的sql语句
  367. $sql = $mysql->getLastSql();
  368. //直接执行sql语句
  369. $sql = "show tables";
  370. $res = $mysql->doSql($sql);
  371. //事务
  372. $mysql->startTrans();
  373. $mysql->where(array('sid'=>102))->update('t_table',array('aa'=>666666));
  374. $mysql->where(array('sid'=>103))->update('t_table',array('bbc'=>'呵呵8888呵呵'));
  375. $mysql->where(array('sid'=>104))->delete('t_table');
  376. $mysql->commit();
  377. */