Db.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. <?php
  2. namespace Typecho;
  3. use Typecho\Db\Adapter;
  4. use Typecho\Db\Query;
  5. use Typecho\Db\Exception as DbException;
  6. /**
  7. * 包含获取数据支持方法的类.
  8. * 必须定义__TYPECHO_DB_HOST__, __TYPECHO_DB_PORT__, __TYPECHO_DB_NAME__,
  9. * __TYPECHO_DB_USER__, __TYPECHO_DB_PASS__, __TYPECHO_DB_CHAR__
  10. *
  11. * @package Db
  12. */
  13. class Db
  14. {
  15. /** 读取数据库 */
  16. public const READ = 1;
  17. /** 写入数据库 */
  18. public const WRITE = 2;
  19. /** 升序方式 */
  20. public const SORT_ASC = 'ASC';
  21. /** 降序方式 */
  22. public const SORT_DESC = 'DESC';
  23. /** 表内连接方式 */
  24. public const INNER_JOIN = 'INNER';
  25. /** 表外连接方式 */
  26. public const OUTER_JOIN = 'OUTER';
  27. /** 表左连接方式 */
  28. public const LEFT_JOIN = 'LEFT';
  29. /** 表右连接方式 */
  30. public const RIGHT_JOIN = 'RIGHT';
  31. /** 数据库查询操作 */
  32. public const SELECT = 'SELECT';
  33. /** 数据库更新操作 */
  34. public const UPDATE = 'UPDATE';
  35. /** 数据库插入操作 */
  36. public const INSERT = 'INSERT';
  37. /** 数据库删除操作 */
  38. public const DELETE = 'DELETE';
  39. /**
  40. * 数据库适配器
  41. * @var Adapter
  42. */
  43. private $adapter;
  44. /**
  45. * 默认配置
  46. *
  47. * @var array
  48. */
  49. private $config;
  50. /**
  51. * 已经连接
  52. *
  53. * @access private
  54. * @var array
  55. */
  56. private $connectedPool;
  57. /**
  58. * 前缀
  59. *
  60. * @access private
  61. * @var string
  62. */
  63. private $prefix;
  64. /**
  65. * 适配器名称
  66. *
  67. * @access private
  68. * @var string
  69. */
  70. private $adapterName;
  71. /**
  72. * 实例化的数据库对象
  73. * @var Db
  74. */
  75. private static $instance;
  76. /**
  77. * 数据库类构造函数
  78. *
  79. * @param mixed $adapterName 适配器名称
  80. * @param string $prefix 前缀
  81. *
  82. * @throws DbException
  83. */
  84. public function __construct($adapterName, string $prefix = 'typecho_')
  85. {
  86. /** 获取适配器名称 */
  87. $adapterName = $adapterName == 'Mysql' ? 'Mysqli' : $adapterName;
  88. $this->adapterName = $adapterName;
  89. /** 数据库适配器 */
  90. $adapterName = '\Typecho\Db\Adapter\\' . str_replace('_', '\\', $adapterName);
  91. if (!call_user_func([$adapterName, 'isAvailable'])) {
  92. throw new DbException("Adapter {$adapterName} is not available");
  93. }
  94. $this->prefix = $prefix;
  95. /** 初始化内部变量 */
  96. $this->connectedPool = [];
  97. $this->config = [
  98. self::READ => [],
  99. self::WRITE => []
  100. ];
  101. //实例化适配器对象
  102. $this->adapter = new $adapterName();
  103. }
  104. /**
  105. * @return Adapter
  106. */
  107. public function getAdapter(): Adapter
  108. {
  109. return $this->adapter;
  110. }
  111. /**
  112. * 获取适配器名称
  113. *
  114. * @access public
  115. * @return string
  116. */
  117. public function getAdapterName(): string
  118. {
  119. return $this->adapterName;
  120. }
  121. /**
  122. * 获取表前缀
  123. *
  124. * @access public
  125. * @return string
  126. */
  127. public function getPrefix(): string
  128. {
  129. return $this->prefix;
  130. }
  131. /**
  132. * @param Config $config
  133. * @param int $op
  134. */
  135. public function addConfig(Config $config, int $op)
  136. {
  137. if ($op & self::READ) {
  138. $this->config[self::READ][] = $config;
  139. }
  140. if ($op & self::WRITE) {
  141. $this->config[self::WRITE][] = $config;
  142. }
  143. }
  144. /**
  145. * getConfig
  146. *
  147. * @param int $op
  148. *
  149. * @return Config
  150. * @throws DbException
  151. */
  152. public function getConfig(int $op): Config
  153. {
  154. if (empty($this->config[$op])) {
  155. /** DbException */
  156. throw new DbException('Missing Database Connection');
  157. }
  158. $key = array_rand($this->config[$op]);
  159. return $this->config[$op][$key];
  160. }
  161. /**
  162. * 重置连接池
  163. *
  164. * @return void
  165. */
  166. public function flushPool()
  167. {
  168. $this->connectedPool = [];
  169. }
  170. /**
  171. * 选择数据库
  172. *
  173. * @param int $op
  174. *
  175. * @return mixed
  176. * @throws DbException
  177. */
  178. public function selectDb(int $op)
  179. {
  180. if (!isset($this->connectedPool[$op])) {
  181. $selectConnectionConfig = $this->getConfig($op);
  182. $selectConnectionHandle = $this->adapter->connect($selectConnectionConfig);
  183. $this->connectedPool[$op] = $selectConnectionHandle;
  184. }
  185. return $this->connectedPool[$op];
  186. }
  187. /**
  188. * 获取SQL词法构建器实例化对象
  189. *
  190. * @return Query
  191. */
  192. public function sql(): Query
  193. {
  194. return new Query($this->adapter, $this->prefix);
  195. }
  196. /**
  197. * 为多数据库提供支持
  198. *
  199. * @access public
  200. * @param array $config 数据库实例
  201. * @param integer $op 数据库操作
  202. * @return void
  203. */
  204. public function addServer(array $config, int $op)
  205. {
  206. $this->addConfig(Config::factory($config), $op);
  207. $this->flushPool();
  208. }
  209. /**
  210. * 获取版本
  211. *
  212. * @param int $op
  213. *
  214. * @return string
  215. * @throws DbException
  216. */
  217. public function getVersion(int $op = self::READ): string
  218. {
  219. return $this->adapter->getVersion($this->selectDb($op));
  220. }
  221. /**
  222. * 设置默认数据库对象
  223. *
  224. * @access public
  225. * @param Db $db 数据库对象
  226. * @return void
  227. */
  228. public static function set(Db $db)
  229. {
  230. self::$instance = $db;
  231. }
  232. /**
  233. * 获取数据库实例化对象
  234. * 用静态变量存储实例化的数据库对象,可以保证数据连接仅进行一次
  235. *
  236. * @return Db
  237. * @throws DbException
  238. */
  239. public static function get(): Db
  240. {
  241. if (empty(self::$instance)) {
  242. /** DbException */
  243. throw new DbException('Missing Database Object');
  244. }
  245. return self::$instance;
  246. }
  247. /**
  248. * 选择查询字段
  249. *
  250. * @param ...$ags
  251. *
  252. * @return Query
  253. * @throws DbException
  254. */
  255. public function select(...$ags): Query
  256. {
  257. $this->selectDb(self::READ);
  258. $args = func_get_args();
  259. return call_user_func_array([$this->sql(), 'select'], $args ?: ['*']);
  260. }
  261. /**
  262. * 更新记录操作(UPDATE)
  263. *
  264. * @param string $table 需要更新记录的表
  265. *
  266. * @return Query
  267. * @throws DbException
  268. */
  269. public function update(string $table): Query
  270. {
  271. $this->selectDb(self::WRITE);
  272. return $this->sql()->update($table);
  273. }
  274. /**
  275. * 删除记录操作(DELETE)
  276. *
  277. * @param string $table 需要删除记录的表
  278. *
  279. * @return Query
  280. * @throws DbException
  281. */
  282. public function delete(string $table): Query
  283. {
  284. $this->selectDb(self::WRITE);
  285. return $this->sql()->delete($table);
  286. }
  287. /**
  288. * 插入记录操作(INSERT)
  289. *
  290. * @param string $table 需要插入记录的表
  291. *
  292. * @return Query
  293. * @throws DbException
  294. */
  295. public function insert(string $table): Query
  296. {
  297. $this->selectDb(self::WRITE);
  298. return $this->sql()->insert($table);
  299. }
  300. /**
  301. * @param $table
  302. * @throws DbException
  303. */
  304. public function truncate($table)
  305. {
  306. $table = preg_replace("/^table\./", $this->prefix, $table);
  307. $this->adapter->truncate($table, $this->selectDb(self::WRITE));
  308. }
  309. /**
  310. * 执行查询语句
  311. *
  312. * @param mixed $query 查询语句或者查询对象
  313. * @param int $op 数据库读写状态
  314. * @param string $action 操作动作
  315. *
  316. * @return mixed
  317. * @throws DbException
  318. */
  319. public function query($query, int $op = self::READ, string $action = self::SELECT)
  320. {
  321. $table = null;
  322. /** 在适配器中执行查询 */
  323. if ($query instanceof Query) {
  324. $action = $query->getAttribute('action');
  325. $table = $query->getAttribute('table');
  326. $op = (self::UPDATE == $action || self::DELETE == $action
  327. || self::INSERT == $action) ? self::WRITE : self::READ;
  328. } elseif (!is_string($query)) {
  329. /** 如果query不是对象也不是字符串,那么将其判断为查询资源句柄,直接返回 */
  330. return $query;
  331. }
  332. /** 选择连接池 */
  333. $handle = $this->selectDb($op);
  334. /** 提交查询 */
  335. $resource = $this->adapter->query($query instanceof Query ?
  336. $query->prepare($query) : $query, $handle, $op, $action, $table);
  337. if ($action) {
  338. //根据查询动作返回相应资源
  339. switch ($action) {
  340. case self::UPDATE:
  341. case self::DELETE:
  342. return $this->adapter->affectedRows($resource, $handle);
  343. case self::INSERT:
  344. return $this->adapter->lastInsertId($resource, $handle);
  345. case self::SELECT:
  346. default:
  347. return $resource;
  348. }
  349. } else {
  350. //如果直接执行查询语句则返回资源
  351. return $resource;
  352. }
  353. }
  354. /**
  355. * 一次取出所有行
  356. *
  357. * @param mixed $query 查询对象
  358. * @param callable|null $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
  359. *
  360. * @return array
  361. * @throws DbException
  362. */
  363. public function fetchAll($query, ?callable $filter = null): array
  364. {
  365. //执行查询
  366. $resource = $this->query($query);
  367. $result = $this->adapter->fetchAll($resource);
  368. return $filter ? array_map($filter, $result) : $result;
  369. }
  370. /**
  371. * 一次取出一行
  372. *
  373. * @param mixed $query 查询对象
  374. * @param callable|null $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
  375. * @return array|null
  376. * @throws DbException
  377. */
  378. public function fetchRow($query, ?callable $filter = null): ?array
  379. {
  380. $resource = $this->query($query);
  381. return ($rows = $this->adapter->fetch($resource)) ?
  382. ($filter ? call_user_func($filter, $rows) : $rows) :
  383. null;
  384. }
  385. /**
  386. * 一次取出一个对象
  387. *
  388. * @param mixed $query 查询对象
  389. * @param array|null $filter 行过滤器函数,将查询的每一行作为第一个参数传入指定的过滤器中
  390. * @return object|null
  391. * @throws DbException
  392. */
  393. public function fetchObject($query, ?array $filter = null): ?object
  394. {
  395. $resource = $this->query($query);
  396. return ($rows = $this->adapter->fetchObject($resource)) ?
  397. ($filter ? call_user_func($filter, $rows) : $rows) :
  398. null;
  399. }
  400. }