Bookmark.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Handles bookmarking SQL queries
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin;
  10. use PhpMyAdmin\DatabaseInterface;
  11. use PhpMyAdmin\Relation;
  12. use PhpMyAdmin\Util;
  13. /**
  14. * Handles bookmarking SQL queries
  15. *
  16. * @package PhpMyAdmin
  17. */
  18. class Bookmark
  19. {
  20. /**
  21. * ID of the bookmark
  22. *
  23. * @var int
  24. */
  25. private $_id;
  26. /**
  27. * Database the bookmark belongs to
  28. *
  29. * @var string
  30. */
  31. private $_database;
  32. /**
  33. * The user to whom the bookmark belongs, empty for public bookmarks
  34. *
  35. * @var string
  36. */
  37. private $_user;
  38. /**
  39. * Label of the bookmark
  40. *
  41. * @var string
  42. */
  43. private $_label;
  44. /**
  45. * SQL query that is bookmarked
  46. *
  47. * @var string
  48. */
  49. private $_query;
  50. /**
  51. * @var DatabaseInterface
  52. */
  53. private $dbi;
  54. /**
  55. * Current user
  56. *
  57. * @var string
  58. */
  59. private $user;
  60. /**
  61. * Bookmark constructor.
  62. *
  63. * @param DatabaseInterface $dbi DatabaseInterface object
  64. * @param string $user Current user
  65. */
  66. public function __construct(DatabaseInterface $dbi, string $user)
  67. {
  68. $this->dbi = $dbi;
  69. $this->user = $user;
  70. }
  71. /**
  72. * Returns the ID of the bookmark
  73. *
  74. * @return int
  75. */
  76. public function getId(): int
  77. {
  78. return (int) $this->_id;
  79. }
  80. /**
  81. * Returns the database of the bookmark
  82. *
  83. * @return string
  84. */
  85. public function getDatabase(): string
  86. {
  87. return $this->_database;
  88. }
  89. /**
  90. * Returns the user whom the bookmark belongs to
  91. *
  92. * @return string
  93. */
  94. public function getUser(): string
  95. {
  96. return $this->_user;
  97. }
  98. /**
  99. * Returns the label of the bookmark
  100. *
  101. * @return string
  102. */
  103. public function getLabel(): string
  104. {
  105. return $this->_label;
  106. }
  107. /**
  108. * Returns the query
  109. *
  110. * @return string
  111. */
  112. public function getQuery(): string
  113. {
  114. return $this->_query;
  115. }
  116. /**
  117. * Adds a bookmark
  118. *
  119. * @return boolean whether the INSERT succeeds or not
  120. *
  121. * @access public
  122. */
  123. public function save(): bool
  124. {
  125. $cfgBookmark = self::getParams($this->user);
  126. if (empty($cfgBookmark)) {
  127. return false;
  128. }
  129. $query = "INSERT INTO " . Util::backquote($cfgBookmark['db'])
  130. . "." . Util::backquote($cfgBookmark['table'])
  131. . " (id, dbase, user, query, label) VALUES (NULL, "
  132. . "'" . $this->dbi->escapeString($this->_database) . "', "
  133. . "'" . $this->dbi->escapeString($this->_user) . "', "
  134. . "'" . $this->dbi->escapeString($this->_query) . "', "
  135. . "'" . $this->dbi->escapeString($this->_label) . "')";
  136. return $this->dbi->query($query, DatabaseInterface::CONNECT_CONTROL);
  137. }
  138. /**
  139. * Deletes a bookmark
  140. *
  141. * @return bool true if successful
  142. *
  143. * @access public
  144. */
  145. public function delete(): bool
  146. {
  147. $cfgBookmark = self::getParams($this->user);
  148. if (empty($cfgBookmark)) {
  149. return false;
  150. }
  151. $query = "DELETE FROM " . Util::backquote($cfgBookmark['db'])
  152. . "." . Util::backquote($cfgBookmark['table'])
  153. . " WHERE id = " . $this->_id;
  154. return $this->dbi->tryQuery($query, DatabaseInterface::CONNECT_CONTROL);
  155. }
  156. /**
  157. * Returns the number of variables in a bookmark
  158. *
  159. * @return int number of variables
  160. */
  161. public function getVariableCount(): int
  162. {
  163. $matches = [];
  164. preg_match_all("/\[VARIABLE[0-9]*\]/", $this->_query, $matches, PREG_SET_ORDER);
  165. return count($matches);
  166. }
  167. /**
  168. * Replace the placeholders in the bookmark query with variables
  169. *
  170. * @param array $variables array of variables
  171. *
  172. * @return string query with variables applied
  173. */
  174. public function applyVariables(array $variables): string
  175. {
  176. // remove comments that encloses a variable placeholder
  177. $query = preg_replace(
  178. '|/\*(.*\[VARIABLE[0-9]*\].*)\*/|imsU',
  179. '${1}',
  180. $this->_query
  181. );
  182. // replace variable placeholders with values
  183. $number_of_variables = $this->getVariableCount();
  184. for ($i = 1; $i <= $number_of_variables; $i++) {
  185. $var = '';
  186. if (! empty($variables[$i])) {
  187. $var = $this->dbi->escapeString($variables[$i]);
  188. }
  189. $query = str_replace('[VARIABLE' . $i . ']', $var, $query);
  190. // backward compatibility
  191. if ($i == 1) {
  192. $query = str_replace('[VARIABLE]', $var, $query);
  193. }
  194. }
  195. return $query;
  196. }
  197. /**
  198. * Defines the bookmark parameters for the current user
  199. *
  200. * @param string $user Current user
  201. *
  202. * @return array|bool the bookmark parameters for the current user
  203. * @access public
  204. */
  205. public static function getParams(string $user)
  206. {
  207. static $cfgBookmark = null;
  208. if (null !== $cfgBookmark) {
  209. return $cfgBookmark;
  210. }
  211. $relation = new Relation($GLOBALS['dbi']);
  212. $cfgRelation = $relation->getRelationsParam();
  213. if ($cfgRelation['bookmarkwork']) {
  214. $cfgBookmark = [
  215. 'user' => $user,
  216. 'db' => $cfgRelation['db'],
  217. 'table' => $cfgRelation['bookmark'],
  218. ];
  219. } else {
  220. $cfgBookmark = false;
  221. }
  222. return $cfgBookmark;
  223. }
  224. /**
  225. * Creates a Bookmark object from the parameters
  226. *
  227. * @param DatabaseInterface $dbi DatabaseInterface object
  228. * @param string $user Current user
  229. * @param array $bkm_fields the properties of the bookmark to add; here,
  230. * $bkm_fields['bkm_sql_query'] is urlencoded
  231. * @param boolean $all_users whether to make the bookmark
  232. * available for all users
  233. *
  234. * @return Bookmark|false
  235. */
  236. public static function createBookmark(
  237. DatabaseInterface $dbi,
  238. string $user,
  239. array $bkm_fields,
  240. bool $all_users = false
  241. ) {
  242. if (! (isset($bkm_fields['bkm_sql_query'])
  243. && strlen($bkm_fields['bkm_sql_query']) > 0
  244. && isset($bkm_fields['bkm_label'])
  245. && strlen($bkm_fields['bkm_label']) > 0)
  246. ) {
  247. return false;
  248. }
  249. $bookmark = new Bookmark($dbi, $user);
  250. $bookmark->_database = $bkm_fields['bkm_database'];
  251. $bookmark->_label = $bkm_fields['bkm_label'];
  252. $bookmark->_query = $bkm_fields['bkm_sql_query'];
  253. $bookmark->_user = $all_users ? '' : $bkm_fields['bkm_user'];
  254. return $bookmark;
  255. }
  256. /**
  257. * Gets the list of bookmarks defined for the current database
  258. *
  259. * @param DatabaseInterface $dbi DatabaseInterface object
  260. * @param string $user Current user
  261. * @param string|bool $db the current database name or false
  262. *
  263. * @return Bookmark[] the bookmarks list
  264. *
  265. * @access public
  266. */
  267. public static function getList(
  268. DatabaseInterface $dbi,
  269. string $user,
  270. $db = false
  271. ): array {
  272. $cfgBookmark = self::getParams($user);
  273. if (empty($cfgBookmark)) {
  274. return [];
  275. }
  276. $query = "SELECT * FROM " . Util::backquote($cfgBookmark['db'])
  277. . "." . Util::backquote($cfgBookmark['table'])
  278. . " WHERE ( `user` = ''"
  279. . " OR `user` = '" . $dbi->escapeString($cfgBookmark['user']) . "' )";
  280. if ($db !== false) {
  281. $query .= " AND dbase = '" . $dbi->escapeString($db) . "'";
  282. }
  283. $query .= " ORDER BY label ASC";
  284. $result = $dbi->fetchResult(
  285. $query,
  286. null,
  287. null,
  288. DatabaseInterface::CONNECT_CONTROL,
  289. DatabaseInterface::QUERY_STORE
  290. );
  291. if (! empty($result)) {
  292. $bookmarks = [];
  293. foreach ($result as $row) {
  294. $bookmark = new Bookmark($dbi, $user);
  295. $bookmark->_id = $row['id'];
  296. $bookmark->_database = $row['dbase'];
  297. $bookmark->_user = $row['user'];
  298. $bookmark->_label = $row['label'];
  299. $bookmark->_query = $row['query'];
  300. $bookmarks[] = $bookmark;
  301. }
  302. return $bookmarks;
  303. }
  304. return [];
  305. }
  306. /**
  307. * Retrieve a specific bookmark
  308. *
  309. * @param DatabaseInterface $dbi DatabaseInterface object
  310. * @param string $user Current user
  311. * @param string $db the current database name
  312. * @param mixed $id an identifier of the bookmark to get
  313. * @param string $id_field which field to look up the identifier
  314. * @param boolean $action_bookmark_all true: get all bookmarks regardless
  315. * of the owning user
  316. * @param boolean $exact_user_match whether to ignore bookmarks with no user
  317. *
  318. * @return Bookmark the bookmark
  319. *
  320. * @access public
  321. *
  322. */
  323. public static function get(
  324. DatabaseInterface $dbi,
  325. string $user,
  326. string $db,
  327. $id,
  328. string $id_field = 'id',
  329. bool $action_bookmark_all = false,
  330. bool $exact_user_match = false
  331. ): ?self {
  332. $cfgBookmark = self::getParams($user);
  333. if (empty($cfgBookmark)) {
  334. return null;
  335. }
  336. $query = "SELECT * FROM " . Util::backquote($cfgBookmark['db'])
  337. . "." . Util::backquote($cfgBookmark['table'])
  338. . " WHERE dbase = '" . $dbi->escapeString($db) . "'";
  339. if (! $action_bookmark_all) {
  340. $query .= " AND (user = '"
  341. . $dbi->escapeString($cfgBookmark['user']) . "'";
  342. if (! $exact_user_match) {
  343. $query .= " OR user = ''";
  344. }
  345. $query .= ")";
  346. }
  347. $query .= " AND " . Util::backquote($id_field)
  348. . " = '" . $dbi->escapeString((string) $id) . "' LIMIT 1";
  349. $result = $dbi->fetchSingleRow($query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL);
  350. if (! empty($result)) {
  351. $bookmark = new Bookmark($dbi, $user);
  352. $bookmark->_id = $result['id'];
  353. $bookmark->_database = $result['dbase'];
  354. $bookmark->_user = $result['user'];
  355. $bookmark->_label = $result['label'];
  356. $bookmark->_query = $result['query'];
  357. return $bookmark;
  358. }
  359. return null;
  360. }
  361. }