You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 lines
9.2 KiB

3 years ago
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace Think\Db\Driver;
  12. use Think\Db\Driver;
  13. /**
  14. * mysql数据库驱动
  15. */
  16. class Mysql extends Driver
  17. {
  18. /**
  19. * 解析pdo连接的dsn信息
  20. * @access public
  21. * @param array $config 连接信息
  22. * @return string
  23. */
  24. protected function parseDsn($config)
  25. {
  26. $dsn = 'mysql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
  27. if (!empty($config['hostport'])) {
  28. $dsn .= ';port=' . $config['hostport'];
  29. } elseif (!empty($config['socket'])) {
  30. $dsn .= ';unix_socket=' . $config['socket'];
  31. }
  32. if (!empty($config['charset'])) {
  33. //为兼容各版本PHP,用两种方式设置编码
  34. $this->options[\PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['charset'];
  35. $dsn .= ';charset=' . $config['charset'];
  36. }
  37. return $dsn;
  38. }
  39. /**
  40. * 取得数据表的字段信息
  41. * @access public
  42. */
  43. public function getFields($tableName)
  44. {
  45. $this->initConnect(true);
  46. list($tableName) = explode(' ', $tableName);
  47. if (strpos($tableName, '.')) {
  48. list($dbName, $tableName) = explode('.', $tableName);
  49. $sql = 'SHOW COLUMNS FROM `' . $dbName . '`.`' . $tableName . '`';
  50. } else {
  51. $sql = 'SHOW COLUMNS FROM `' . $tableName . '`';
  52. }
  53. $result = $this->query($sql);
  54. $info = array();
  55. if ($result) {
  56. foreach ($result as $key => $val) {
  57. if (\PDO::CASE_LOWER != $this->_linkID->getAttribute(\PDO::ATTR_CASE)) {
  58. $val = array_change_key_case($val, CASE_LOWER);
  59. }
  60. $info[$val['field']] = array(
  61. 'name' => $val['field'],
  62. 'type' => $val['type'],
  63. 'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
  64. 'default' => $val['default'],
  65. 'primary' => (strtolower($val['key']) == 'pri'),
  66. 'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
  67. );
  68. }
  69. }
  70. return $info;
  71. }
  72. /**
  73. * 取得数据库的表信息
  74. * @access public
  75. */
  76. public function getTables($dbName = '')
  77. {
  78. $sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
  79. $result = $this->query($sql);
  80. $info = array();
  81. foreach ($result as $key => $val) {
  82. $info[$key] = current($val);
  83. }
  84. return $info;
  85. }
  86. /**
  87. * 字段和表名处理
  88. * @access public
  89. * @param string $key
  90. * @param bool $strict
  91. * @return string
  92. */
  93. public function parseKey($key, $strict = false)
  94. {
  95. if (is_int($key)) {
  96. return $key;
  97. }
  98. $key = trim($key);
  99. if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
  100. E('not support data:' . $key);
  101. }
  102. if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
  103. $key = '`' . $key . '`';
  104. }
  105. return $key;
  106. }
  107. /**
  108. * 随机排序
  109. * @access protected
  110. * @return string
  111. */
  112. protected function parseRand()
  113. {
  114. return 'rand()';
  115. }
  116. /**
  117. * 批量插入记录
  118. * @access public
  119. * @param mixed $dataSet 数据集
  120. * @param array $options 参数表达式
  121. * @param boolean $replace 是否replace
  122. * @return false | integer
  123. */
  124. public function insertAll($dataSet, $options = array(), $replace = false)
  125. {
  126. $values = array();
  127. $this->model = $options['model'];
  128. if (!is_array($dataSet[0])) {
  129. return false;
  130. }
  131. $this->parseBind(!empty($options['bind']) ? $options['bind'] : array());
  132. $fields = array_map(array($this, 'parseKey'), array_keys($dataSet[0]));
  133. foreach ($dataSet as $data) {
  134. $value = array();
  135. foreach ($data as $key => $val) {
  136. if (is_array($val) && 'exp' == $val[0]) {
  137. $value[] = $val[1];
  138. } elseif (is_null($val)) {
  139. $value[] = 'NULL';
  140. } elseif (is_scalar($val)) {
  141. if (0 === strpos($val, ':') && in_array($val, array_keys($this->bind))) {
  142. $value[] = $this->parseValue($val);
  143. } else {
  144. $name = count($this->bind);
  145. $value[] = ':' . $name;
  146. $this->bindParam($name, $val);
  147. }
  148. }
  149. }
  150. $values[] = '(' . implode(',', $value) . ')';
  151. }
  152. // 兼容数字传入方式
  153. $replace = (is_numeric($replace) && $replace > 0) ? true : $replace;
  154. $sql = (true === $replace ? 'REPLACE' : 'INSERT') . ' INTO ' . $this->parseTable($options['table']) . ' (' . implode(',', $fields) . ') VALUES ' . implode(',', $values) . $this->parseDuplicate($replace);
  155. $sql .= $this->parseComment(!empty($options['comment']) ? $options['comment'] : '');
  156. return $this->execute($sql, !empty($options['fetch_sql']) ? true : false);
  157. }
  158. /**
  159. * ON DUPLICATE KEY UPDATE 分析
  160. * @access protected
  161. * @param mixed $duplicate
  162. * @return string
  163. */
  164. protected function parseDuplicate($duplicate)
  165. {
  166. // 布尔值或空则返回空字符串
  167. if (is_bool($duplicate) || empty($duplicate)) {
  168. return '';
  169. }
  170. if (is_string($duplicate)) {
  171. // field1,field2 转数组
  172. $duplicate = explode(',', $duplicate);
  173. } elseif (is_object($duplicate)) {
  174. // 对象转数组
  175. $duplicate = get_class_vars($duplicate);
  176. }
  177. $updates = array();
  178. foreach ((array) $duplicate as $key => $val) {
  179. if (is_numeric($key)) {
  180. // array('field1', 'field2', 'field3') 解析为 ON DUPLICATE KEY UPDATE field1=VALUES(field1), field2=VALUES(field2), field3=VALUES(field3)
  181. $updates[] = $this->parseKey($val) . "=VALUES(" . $this->parseKey($val) . ")";
  182. } else {
  183. if (is_scalar($val)) // 兼容标量传值方式
  184. {
  185. $val = array('value', $val);
  186. }
  187. if (!isset($val[1]) && !is_null($val[1])) {
  188. continue;
  189. }
  190. switch ($val[0]) {
  191. case 'exp': // 表达式
  192. $updates[] = $this->parseKey($key) . "=($val[1])";
  193. break;
  194. case 'value': // 值
  195. default:
  196. $name = count($this->bind);
  197. $updates[] = $this->parseKey($key) . "=:" . $name;
  198. $this->bindParam($name, $val[1]);
  199. break;
  200. }
  201. }
  202. }
  203. if (empty($updates)) {
  204. return '';
  205. }
  206. return " ON DUPLICATE KEY UPDATE " . join(', ', $updates);
  207. }
  208. /**
  209. * 执行存储过程查询 返回多个数据集
  210. * @access public
  211. * @param string $str sql指令
  212. * @param boolean $fetchSql 不执行只是获取SQL
  213. * @return mixed
  214. */
  215. public function procedure($str, $fetchSql = false)
  216. {
  217. $this->initConnect(false);
  218. $this->_linkID->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_WARNING);
  219. if (!$this->_linkID) {
  220. return false;
  221. }
  222. $this->queryStr = $str;
  223. if ($fetchSql) {
  224. return $this->queryStr;
  225. }
  226. //释放前次的查询结果
  227. if (!empty($this->PDOStatement)) {
  228. $this->free();
  229. }
  230. $this->queryTimes++;
  231. N('db_query', 1); // 兼容代码
  232. // 调试开始
  233. $this->debug(true);
  234. $this->PDOStatement = $this->_linkID->prepare($str);
  235. if (false === $this->PDOStatement) {
  236. $this->error();
  237. return false;
  238. }
  239. try {
  240. $result = $this->PDOStatement->execute();
  241. // 调试结束
  242. $this->debug(false);
  243. do {
  244. $result = $this->PDOStatement->fetchAll(\PDO::FETCH_ASSOC);
  245. if ($result) {
  246. $resultArr[] = $result;
  247. }
  248. } while ($this->PDOStatement->nextRowset());
  249. $this->_linkID->setAttribute(\PDO::ATTR_ERRMODE, $this->options[\PDO::ATTR_ERRMODE]);
  250. return $resultArr;
  251. } catch (\PDOException $e) {
  252. $this->error();
  253. $this->_linkID->setAttribute(\PDO::ATTR_ERRMODE, $this->options[\PDO::ATTR_ERRMODE]);
  254. return false;
  255. }
  256. }
  257. }