PageRenderTime 30ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/phpmyfaq/src/phpMyFAQ/Database/Mysqli.php

http://github.com/thorsten/phpMyFAQ
PHP | 401 lines | 196 code | 41 blank | 164 comment | 18 complexity | 18ed4449c1af6fddb9e51ffec3e6c24a MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /**
  3. * The phpMyFAQ\Database\Mysqli class provides methods and functions for MySQL and
  4. * MariaDB databases.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public License,
  7. * v. 2.0. If a copy of the MPL was not distributed with this file, You can
  8. * obtain one at http://mozilla.org/MPL/2.0/.
  9. *
  10. * @package phpMyFAQ
  11. * @author Thorsten Rinne <thorsten@phpmyfaq.de>
  12. * @author David Soria Parra <dsoria@gmx.net>
  13. * @copyright 2005-2021 phpMyFAQ Team
  14. * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
  15. * @link https://www.phpmyfaq.de
  16. */
  17. namespace phpMyFAQ\Database;
  18. use mysqli_result;
  19. use phpMyFAQ\Database;
  20. use phpMyFAQ\Core\Exception;
  21. use phpMyFAQ\Utils;
  22. /**
  23. * Class Mysqli
  24. *
  25. * @package phpMyFAQ\Database
  26. */
  27. class Mysqli implements DatabaseDriver
  28. {
  29. /**
  30. * Tables.
  31. *
  32. * @var array
  33. */
  34. public $tableNames = [];
  35. /**
  36. * The connection object.
  37. *
  38. * @var \mysqli
  39. */
  40. private $conn = false;
  41. /**
  42. * The query log string.
  43. *
  44. * @var string
  45. */
  46. private $sqllog = '';
  47. /**
  48. * Connects to the database.
  49. *
  50. * @param string $host Hostname or path to socket
  51. * @param string $user Username
  52. * @param string $password Password
  53. * @param string $database Database name
  54. * @param int|null $port
  55. * @return null|bool true, if connected, otherwise false
  56. * @throws Exception
  57. */
  58. public function connect(string $host, string $user, string $password, $database = '', $port = 3306): ?bool
  59. {
  60. if (substr($host, 0, 1) === '/') {
  61. // Connect to MySQL via socket
  62. $this->conn = new \mysqli(null, $user, $password, null, $port, $host);
  63. } else {
  64. // Connect to MySQL via network
  65. $this->conn = new \mysqli($host, $user, $password, null, $port);
  66. }
  67. if ($this->conn->connect_error) {
  68. Database::errorPage($this->conn->connect_errno . ': ' . $this->conn->connect_error);
  69. die();
  70. }
  71. // change character set to UTF-8
  72. if (!$this->conn->set_charset('utf8')) {
  73. Database::errorPage($this->error());
  74. }
  75. if ('' !== $database) {
  76. if (!$this->conn->select_db($database)) {
  77. throw new Exception('Cannot connect to database ' . $database);
  78. }
  79. }
  80. return true;
  81. }
  82. /**
  83. * Returns the error string.
  84. *
  85. * @return string
  86. */
  87. public function error(): string
  88. {
  89. return $this->conn->error;
  90. }
  91. /**
  92. * Escapes a string for use in a query.
  93. *
  94. * @param string
  95. *
  96. * @return string
  97. */
  98. public function escape($string): string
  99. {
  100. return $this->conn->real_escape_string($string);
  101. }
  102. /**
  103. * Fetch a result row as an object.
  104. *
  105. * This function fetches a result as an associative array.
  106. *
  107. * @param mixed $result
  108. *
  109. * @return array
  110. */
  111. public function fetchArray($result): ?array
  112. {
  113. return $result->fetch_assoc();
  114. }
  115. /**
  116. * Fetch a result row.
  117. * @param $result
  118. * @return false|mixed
  119. */
  120. public function fetchRow($result)
  121. {
  122. return $result->fetch_row()[0] ?? false;
  123. }
  124. /**
  125. * Fetches a complete result as an object.
  126. *
  127. * @param mysqli_result $result Result set
  128. * @return array
  129. * @throws Exception
  130. */
  131. public function fetchAll($result): ?array
  132. {
  133. $ret = [];
  134. if (false === $result) {
  135. throw new Exception('Error while fetching result: ' . $this->error());
  136. }
  137. while ($row = $this->fetchObject($result)) {
  138. $ret[] = $row;
  139. }
  140. return $ret;
  141. }
  142. /**
  143. * Fetch a result row as an object.
  144. *
  145. * This function fetches a result row as an object.
  146. *
  147. * @param mysqli_result $result
  148. *
  149. * @return mixed
  150. * @throws Exception
  151. */
  152. public function fetchObject($result)
  153. {
  154. if ($result instanceof mysqli_result) {
  155. return $result->fetch_object();
  156. }
  157. throw new Exception($this->error());
  158. }
  159. /**
  160. * Number of rows in a result.
  161. *
  162. * @param mysqli_result $result
  163. *
  164. * @return int
  165. */
  166. public function numRows($result): int
  167. {
  168. if ($result instanceof mysqli_result) {
  169. return $result->num_rows;
  170. } else {
  171. return 0;
  172. }
  173. }
  174. /**
  175. * Logs the queries.
  176. *
  177. * @return string
  178. */
  179. public function log(): string
  180. {
  181. return $this->sqllog;
  182. }
  183. /**
  184. * This function returns the table status.
  185. *
  186. * @param string $prefix Table prefix
  187. *
  188. * @return array
  189. */
  190. public function getTableStatus($prefix = ''): array
  191. {
  192. $status = [];
  193. foreach ($this->getTableNames($prefix) as $table) {
  194. $status[$table] = $this->getOne('SELECT count(*) FROM ' . $table);
  195. }
  196. return $status;
  197. }
  198. /**
  199. * Returns an array with all table names.
  200. *
  201. * @todo Have to be refactored because of https://github.com/thorsten/phpMyFAQ/issues/965
  202. *
  203. * @param string $prefix Table prefix
  204. *
  205. * @return string[]
  206. */
  207. public function getTableNames($prefix = ''): array
  208. {
  209. return $this->tableNames = [
  210. $prefix . 'faqadminlog',
  211. $prefix . 'faqattachment',
  212. $prefix . 'faqattachment_file',
  213. $prefix . 'faqcaptcha',
  214. $prefix . 'faqcategories',
  215. $prefix . 'faqcategoryrelations',
  216. $prefix . 'faqcategory_group',
  217. $prefix . 'faqcategory_news',
  218. $prefix . 'faqcategory_order',
  219. $prefix . 'faqcategory_user',
  220. $prefix . 'faqchanges',
  221. $prefix . 'faqcomments',
  222. $prefix . 'faqconfig',
  223. $prefix . 'faqdata',
  224. $prefix . 'faqdata_group',
  225. $prefix . 'faqdata_revisions',
  226. $prefix . 'faqdata_tags',
  227. $prefix . 'faqdata_user',
  228. $prefix . 'faqglossary',
  229. $prefix . 'faqgroup',
  230. $prefix . 'faqgroup_right',
  231. $prefix . 'faqinstances',
  232. $prefix . 'faqinstances_config',
  233. $prefix . 'faqmeta',
  234. $prefix . 'faqnews',
  235. $prefix . 'faqquestions',
  236. $prefix . 'faqright',
  237. $prefix . 'faqsearches',
  238. $prefix . 'faqsections',
  239. $prefix . 'faqsection_category',
  240. $prefix . 'faqsection_group',
  241. $prefix . 'faqsection_news',
  242. $prefix . 'faqsessions',
  243. $prefix . 'faqstopwords',
  244. $prefix . 'faqtags',
  245. $prefix . 'faquser',
  246. $prefix . 'faquserdata',
  247. $prefix . 'faquserlogin',
  248. $prefix . 'faquser_group',
  249. $prefix . 'faquser_right',
  250. $prefix . 'faqvisits',
  251. $prefix . 'faqvoting',
  252. ];
  253. }
  254. /**
  255. * Returns just one row.
  256. *
  257. * @param string $query
  258. *
  259. * @return string
  260. */
  261. private function getOne($query): string
  262. {
  263. $row = $this->conn->query($query)->fetch_row();
  264. return $row[0];
  265. }
  266. /**
  267. * This function is a replacement for MySQL's auto-increment so that
  268. * we don't need it anymore.
  269. *
  270. * @param string $table The name of the table
  271. * @param string $id The name of the ID column
  272. *
  273. * @return int
  274. */
  275. public function nextId($table, $id): int
  276. {
  277. $select = sprintf(
  278. '
  279. SELECT
  280. MAX(%s) AS current_id
  281. FROM
  282. %s',
  283. $id,
  284. $table
  285. );
  286. $result = $this->query($select);
  287. if ($result instanceof mysqli_result) {
  288. $current = $result->fetch_row();
  289. } else {
  290. $current = [0];
  291. }
  292. return $current[0] + 1;
  293. }
  294. /**
  295. * This function sends a query to the database.
  296. *
  297. * @param string $query
  298. * @param int $offset
  299. * @param int $rowcount
  300. *
  301. * @return mysqli_result $result
  302. */
  303. public function query(string $query, $offset = 0, $rowcount = 0)
  304. {
  305. if (DEBUG) {
  306. $this->sqllog .= Utils::debug($query);
  307. }
  308. if (0 < $rowcount) {
  309. $query .= sprintf(' LIMIT %d,%d', $offset, $rowcount);
  310. }
  311. $result = $this->conn->query($query);
  312. if (false === $result) {
  313. $this->sqllog .= $this->conn->errno . ': ' . $this->error();
  314. }
  315. return $result;
  316. }
  317. /**
  318. * Returns the client version string.
  319. *
  320. * @return string
  321. */
  322. public function clientVersion(): string
  323. {
  324. return $this->conn->get_client_info();
  325. }
  326. /**
  327. * Returns the server version string.
  328. *
  329. * @return string
  330. */
  331. public function serverVersion(): string
  332. {
  333. return $this->conn->server_info;
  334. }
  335. /**
  336. * Closes the connection to the database.
  337. */
  338. public function close()
  339. {
  340. if (is_resource($this->conn)) {
  341. $this->conn->close();
  342. }
  343. }
  344. /**
  345. * Destructor.
  346. */
  347. public function __destruct()
  348. {
  349. if (is_resource($this->conn)) {
  350. $this->conn->close();
  351. }
  352. }
  353. /**
  354. * @return string
  355. */
  356. public function now(): string
  357. {
  358. return 'NOW()';
  359. }
  360. }