111
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.
 
 
 
 
 

299 lines
9.6 KiB

  1. <?php
  2. /**
  3. * @copyright (C)2016-2099 Hnaoyun Inc.
  4. * @author XingMeng
  5. * @email hnxsh@foxmail.com
  6. * @date 2017年8月30日
  7. * 数据库PDO驱动
  8. */
  9. namespace core\database;
  10. use core\basic\Config;
  11. class Pdo implements Builder
  12. {
  13. protected static $pdo;
  14. protected $master;
  15. protected $slave;
  16. protected $begin = false;
  17. private function __construct()
  18. {}
  19. public function __destruct()
  20. {
  21. if ($this->begin) { // 存在待提交的事务时自动进行提交
  22. $this->commit();
  23. }
  24. }
  25. // 获取单一实例,使用单一实例数据库连接类
  26. public static function getInstance()
  27. {
  28. if (! self::$pdo) {
  29. self::$pdo = new self();
  30. }
  31. return self::$pdo;
  32. }
  33. // 连接数据库,接受数据库连接参数,返回数据库连接对象
  34. public function conn($cfg)
  35. {
  36. if (! extension_loaded('PDO')) {
  37. die('未检测到您服务器环境的PDO数据库扩展,请检查php.ini中是否已经开启对应的数据库扩展!');
  38. }
  39. $charset = Config::get('database.charset') ?: 'utf8';
  40. switch (Config::get('database.type')) {
  41. case 'pdo_mysql':
  42. $dsn = 'mysql:host=' . $cfg['host'] . ';port=' . $cfg['port'] . ';dbname=' . $cfg['dbname'] . ';charset=' . $charset;
  43. try {
  44. $conn = new \PDO($dsn, $cfg['user'], $cfg['passwd']);
  45. } catch (\PDOException $e) {
  46. error('PDO方式连接MySQL数据库错误:' . iconv('gbk', 'utf-8', $e->getMessage()));
  47. }
  48. break;
  49. case 'pdo_sqlite':
  50. $dsn = 'sqlite:' . ROOT_PATH . $cfg['dbname'];
  51. try {
  52. $conn = new \PDO($dsn);
  53. } catch (\PDOException $e) {
  54. error('PDO方式连接Sqlite数据库错误:' . iconv('gbk', 'utf-8', $e->getMessage()));
  55. }
  56. break;
  57. case 'pdo_pgsql':
  58. $dsn = 'pgsql:host=' . $cfg['host'] . ';port=' . $cfg['port'] . ';dbname=' . $cfg['dbname'];
  59. try {
  60. $conn = new \PDO($dsn, $cfg['user'], $cfg['passwd']);
  61. } catch (\PDOException $e) {
  62. error('PDO方式连接Pgsql数据库错误:' . iconv('gbk', 'utf-8', $e->getMessage()));
  63. }
  64. break;
  65. default:
  66. $dsn = Config::get('database.dsn');
  67. try {
  68. $conn = new \PDO($dsn, $cfg['user'], $cfg['passwd']);
  69. } catch (\PDOException $e) {
  70. error('PDO方式连接数据库错误:' . iconv('gbk', 'utf-8', $e->getMessage()));
  71. }
  72. break;
  73. }
  74. return $conn;
  75. }
  76. // 关闭自动提交,开启事务模式
  77. public function begin()
  78. {
  79. $this->master->beginTransaction();
  80. $this->begin = true;
  81. }
  82. // 提交事务
  83. public function commit()
  84. {
  85. $this->master->commit();
  86. $this->begin = false;
  87. }
  88. // 执行SQL语句,接受完整SQL语句,返回结果集对象
  89. public function query($sql, $type = 'master')
  90. {
  91. $time_s = microtime(true);
  92. switch ($type) {
  93. case 'master':
  94. if (! $this->master) {
  95. $cfg = Config::get('database');
  96. $this->master = $this->conn($cfg);
  97. if ($cfg['type'] == 'pdo_mysql') {
  98. $this->master->exec("SET sql_mode='NO_ENGINE_SUBSTITUTION'"); // MySql写入规避严格模式
  99. }
  100. }
  101. // sqlite时自动启动事务
  102. if ($cfg['type'] == 'pdo_sqlite' && ! $this->begin) {
  103. $this->begin();
  104. } elseif ($cfg['type'] == 'pdo_mysql' && Config::get('database.transaction') && ! $this->begin) { // 根据配置开启mysql事务,注意需要是InnoDB引擎
  105. $this->begin();
  106. }
  107. $result = $this->master->exec($sql);
  108. if ($result === false) {
  109. $this->error($sql, 'master');
  110. }
  111. break;
  112. case 'slave':
  113. if (! $this->slave) {
  114. // 未设置从服务器时直接读取主数据库配置
  115. if (! $cfg = Config::get('database.slave')) {
  116. $cfg = Config::get('database');
  117. } else {
  118. // 随机选择从数据库
  119. if (is_multi_array($cfg)) {
  120. $count = count($cfg);
  121. $cfg = $cfg['slave' . mt_rand(1, $count)];
  122. }
  123. }
  124. $this->slave = $this->conn($cfg);
  125. }
  126. $result = $this->slave->query($sql) or $this->error($sql, 'slave');
  127. break;
  128. }
  129. return $result;
  130. }
  131. // 数据是否存在模型,接受完整SQL语句,返回boolean数据
  132. public function isExist($sql)
  133. {
  134. $result = $this->query($sql, 'slave');
  135. if ($result->fetch()) {
  136. return true;
  137. } else {
  138. return false;
  139. }
  140. }
  141. // 获取记录总量模型,接受数据库表名,返回int数据
  142. public function rows($table)
  143. {
  144. $sql = "SELECT count(*) FROM $table";
  145. $result = $this->query($sql, 'slave');
  146. if (! ! $row = $result->fetch(\PDO::FETCH_NUM)) {
  147. return $row[0];
  148. } else {
  149. return 0;
  150. }
  151. }
  152. // 读取字段数量模型,接受数据库表名,返回int数据
  153. public function fields($table)
  154. {
  155. $sql = "SELECT * FROM $table LIMIT 1";
  156. $result = $this->query($sql, 'slave');
  157. if ($result) {
  158. return $result->columnCount();
  159. } else {
  160. return false;
  161. }
  162. }
  163. /**
  164. * 获取表信息,接受数据库表名,返回表字段信息数组
  165. *
  166. * @param $table 表名
  167. */
  168. public function tableFields($table)
  169. {
  170. $rows = array();
  171. switch (Config::get('database.type')) {
  172. case 'pdo_mysql':
  173. $sql = "describe $table";
  174. $result = $this->query($sql, 'slave');
  175. while (! ! $row = $result->fetchObject()) {
  176. $rows[] = $row->Field;
  177. }
  178. break;
  179. case 'pdo_sqlite':
  180. $sql = "pragma table_info($table)";
  181. $result = $this->query($sql, 'slave');
  182. while (! ! $row = $result->fetchObject()) {
  183. $rows[] = $row->name;
  184. }
  185. break;
  186. case 'pdo_pgsql':
  187. $sql = "SELECT column_name FROM information_schema.columns WHERE table_name ='$table'";
  188. $result = $this->query($sql, 'slave');
  189. while (! ! $row = $result->fetchObject()) {
  190. $rows[] = $row->column_name;
  191. }
  192. break;
  193. default:
  194. return array();
  195. }
  196. return $rows;
  197. }
  198. /**
  199. * 查询一条数据模型,接受完整SQL语句,有数据返回对象数组,否则空数组
  200. * @$type 可以是MYSQLI_ASSOC(FETCH_ASSOC) ,MYSQLI_NUM(FETCH_NUM) ,MYSQLI_BOTH(FETCH_BOTH),不设置则返回对象模式
  201. */
  202. public function one($sql, $type = null)
  203. {
  204. $result = $this->query($sql, 'slave');
  205. $row = array();
  206. if ($type) {
  207. $type ++; // 与mysqli统一返回类型设置
  208. $row = $result->fetch($type);
  209. } else {
  210. $row = $result->fetchObject();
  211. }
  212. return $row;
  213. }
  214. /**
  215. * 查询多条数据模型,接受完整SQL语句,有数据返回二维对象数组,否则空数组
  216. * @$type 可以是MYSQLI_ASSOC(FETCH_ASSOC) ,MYSQLI_NUM(FETCH_NUM) ,MYSQLI_BOTH(FETCH_BOTH),不设置则返回对象模式
  217. */
  218. public function all($sql, $type = null)
  219. {
  220. $result = $this->query($sql, 'slave');
  221. $rows = array();
  222. if ($type) {
  223. $type ++; // 与mysqli统一返回类型设置
  224. $rows = $result->fetchAll($type);
  225. } else {
  226. while (! ! $row = $result->fetchObject()) {
  227. $rows[] = $row;
  228. }
  229. }
  230. return $rows;
  231. }
  232. // 数据增、删、改模型,接受完整SQL语句,返回影响的行数的int数据
  233. public function amd($sql)
  234. {
  235. $result = $this->query($sql, 'master');
  236. if ($result > 0) {
  237. return $result;
  238. } else {
  239. return 0;
  240. }
  241. }
  242. // 最近一次插入数据的自增字段值,返回int数据
  243. public function insertId()
  244. {
  245. return $this->master->lastInsertId();
  246. }
  247. // 执行多条SQL模型,成功返回true,否则false
  248. public function multi($sql)
  249. {
  250. $sqls = explode(';', $sql);
  251. foreach ($sqls as $key => $value) {
  252. $result = $this->query($value, 'master');
  253. }
  254. if ($result) {
  255. return true;
  256. } else {
  257. return false;
  258. }
  259. }
  260. // 显示执行错误
  261. protected function error($sql, $conn)
  262. {
  263. $errs = $this->$conn->errorInfo();
  264. $err = '错误:' . $errs[2] . ',';
  265. if (preg_match('/XPATH/i', $err)) {
  266. $err = '';
  267. }
  268. if ($this->begin) { // 如果是事务模式,发生错误,则回滚
  269. $this->$conn->rollBack();
  270. $this->begin = false;
  271. }
  272. error('执行SQL发生错误!' . $err . '语句:' . $sql);
  273. }
  274. }