Feature Request #3187076
[phpmyadmin-regexreplace.git] / libraries / List_Database.class.php
blob3354a1e8eb99b4590d8f77ddce7e72d01369e531
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * holds the PMA_List_Database class
6 * @package phpMyAdmin
7 */
9 /**
10 * the list base class
12 require_once './libraries/List.class.php';
14 /**
15 * handles database lists
17 * <code>
18 * $PMA_List_Database = new PMA_List_Database($userlink, $controllink);
19 * </code>
21 * @todo this object should be attached to the PMA_Server object
22 * @todo ? make use of INFORMATION_SCHEMA
23 * @todo ? support --skip-showdatabases and user has only global rights
24 * @access public
25 * @since phpMyAdmin 2.9.10
26 * @package phpMyAdmin
28 /*public*/ class PMA_List_Database extends PMA_List
30 /**
31 * @var mixed database link resource|object to be used
33 protected $_db_link = null;
35 /**
36 * @var mixed user database link resource|object
38 protected $_db_link_user = null;
40 /**
41 * @var mixed controluser database link resource|object
43 protected $_db_link_control = null;
45 /**
46 * @var boolean whether SHOW DATABASES is disabled or not
47 * @access protected
49 protected $_show_databases_disabled = false;
51 /**
52 * @var string command to retrieve databases from server
54 protected $_command = null;
56 /**
57 * Constructor
59 * @uses PMA_List_Database::$_db_link
60 * @uses PMA_List_Database::$_db_link_user
61 * @uses PMA_List_Database::$_db_link_control
62 * @uses PMA_List_Database::build()
63 * @param mixed $db_link_user user database link resource|object
64 * @param mixed $db_link_control control database link resource|object
66 public function __construct($db_link_user = null, $db_link_control = null)
68 $this->_db_link = $db_link_user;
69 $this->_db_link_user = $db_link_user;
70 $this->_db_link_control = $db_link_control;
72 parent::__construct();
73 $this->build();
76 /**
77 * checks if the configuration wants to hide some databases
79 * @todo temporaly use this docblock to test how to doc $GLOBALS
80 * @uses PMA_List_Database::$items
81 * @uses preg_match()
82 * @uses $cfg['Server']['hide_db']
84 protected function _checkHideDatabase()
86 if (empty($GLOBALS['cfg']['Server']['hide_db'])) {
87 return;
90 foreach ($this->getArrayCopy() as $key => $db) {
91 if (preg_match('/' . $GLOBALS['cfg']['Server']['hide_db'] . '/', $db)) {
92 $this->offsetUnset($key);
97 /**
98 * retrieves database list from server
100 * @todo we could also search mysql tables if all fail?
101 * @uses PMA_List_Database::$_show_databases_disabled for not retrying if SHOW DATABASES is disabled
102 * @uses PMA_List_Database::$_db_link
103 * @uses PMA_List_Database::$_db_link_control in case of SHOW DATABASES is disabled for userlink
104 * @uses PMA_DBI_fetch_result()
105 * @uses PMA_DBI_getError()
106 * @uses natsort()
107 * @uses sort()
108 * @uses $cfg['NaturalOrder']
109 * @uses $GLOBALS['error_showdatabases']
110 * @uses $GLOBALS['errno']
111 * @param string $like_db_name usally a db_name containing wildcards
113 protected function _retrieve($like_db_name = null)
115 if ($this->_show_databases_disabled) {
116 return array();
119 if (null !== $like_db_name) {
120 $command = "SHOW DATABASES LIKE '" . $like_db_name . "'";
121 } elseif (null === $this->_command) {
122 $command = str_replace('#user#', $GLOBALS['cfg']['Server']['user'],
123 $GLOBALS['cfg']['Server']['ShowDatabasesCommand']);
124 $this->_command = $command;
125 } else {
126 $command = $this->_command;
129 $database_list = PMA_DBI_fetch_result($command, null, null, $this->_db_link);
130 PMA_DBI_getError();
132 if ($GLOBALS['errno'] !== 0) {
133 // failed to get database list, try the control user
134 // (hopefully there is one and he has SHOW DATABASES right)
135 $this->_db_link = $this->_db_link_control;
136 $database_list = PMA_DBI_fetch_result($command, null, null, $this->_db_link);
138 PMA_DBI_getError();
140 if ($GLOBALS['errno'] !== 0) {
141 // failed! we will display a warning that phpMyAdmin could not safely
142 // retrieve database list, the admin has to setup a control user or
143 // allow SHOW DATABASES
144 $GLOBALS['error_showdatabases'] = true;
145 $this->_show_databases_disabled = true;
149 if ($GLOBALS['cfg']['NaturalOrder']) {
150 natsort($database_list);
151 } else {
152 // need to sort anyway, otherwise information_schema
153 // goes at the top
154 sort($database_list);
157 return $database_list;
161 * builds up the list
163 * @uses PMA_List_Database::$items to initialize it
164 * @uses PMA_List_Database::_checkOnlyDatabase()
165 * @uses PMA_List_Database::_retrieve()
166 * @uses PMA_List_Database::_checkHideDatabase()
167 * @uses exchangeArray()
169 public function build()
171 if (! $this->_checkOnlyDatabase()) {
172 $items = $this->_retrieve();
173 $this->exchangeArray($items);
176 $this->_checkHideDatabase();
180 * checks the only_db configuration
182 * @uses PMA_List_Database::$_show_databases_disabled
183 * @uses PMA_List_Database::$items
184 * @uses PMA_List_Database::_retrieve()
185 * @uses PMA_unescape_mysql_wildcards()
186 * @uses preg_match()
187 * @uses array_diff()
188 * @uses array_merge()
189 * @uses is_array()
190 * @uses strlen()
191 * @uses is_string()
192 * @uses $cfg['Server']['only_db']
193 * @return boolean false if there is no only_db, otherwise true
195 protected function _checkOnlyDatabase()
197 if (is_string($GLOBALS['cfg']['Server']['only_db'])
198 && strlen($GLOBALS['cfg']['Server']['only_db'])) {
199 $GLOBALS['cfg']['Server']['only_db'] = array(
200 $GLOBALS['cfg']['Server']['only_db']
204 if (! is_array($GLOBALS['cfg']['Server']['only_db'])) {
205 return false;
208 $items = array();
210 foreach ($GLOBALS['cfg']['Server']['only_db'] as $each_only_db) {
211 if ($each_only_db === '*' && ! $this->_show_databases_disabled) {
212 // append all not already listed dbs to the list
213 $items = array_merge($items,
214 array_diff($this->_retrieve(), $items));
215 // there can only be one '*', and this can only be last
216 break;
219 // check if the db name contains wildcard,
220 // thus containing not escaped _ or %
221 if (! preg_match('/(^|[^\\\\])(_|%)/', $each_only_db)) {
222 // ... not contains wildcard
223 $items[] = PMA_unescape_mysql_wildcards($each_only_db);
224 continue;
227 if (! $this->_show_databases_disabled) {
228 $items = array_merge($items, $this->_retrieve($each_only_db));
229 continue;
232 // @todo induce error, about not using wildcards with SHOW DATABASE disabled?
235 $this->exchangeArray($items);
237 return true;
241 * returns default item
243 * @uses PMA_List::getEmpty()
244 * @uses $GLOBALS['db']
245 * @uses strlen()
246 * @return string default item
248 public function getDefault()
250 if (strlen($GLOBALS['db'])) {
251 return $GLOBALS['db'];
254 return $this->getEmpty();
258 * returns array with dbs grouped with extended infos
260 * @uses $GLOBALS['PMA_List_Database']
261 * @uses $GLOBALS['cfgRelation']['commwork']
262 * @uses $cfg['ShowTooltip']
263 * @uses $cfg['LeftFrameDBTree']
264 * @uses $cfg['LeftFrameDBSeparator']
265 * @uses $cfg['ShowTooltipAliasDB']
266 * @uses PMA_getTableCount()
267 * @uses PMA_getDbComment()
268 * @uses is_array()
269 * @uses implode()
270 * @uses strstr()
271 * @uses explode()
272 * @param integer $offset
273 * @param integer $count
274 * @return array db list
276 public function getGroupedDetails($offset, $count)
278 $dbgroups = array();
279 $parts = array();
281 if ($GLOBALS['cfg']['ShowTooltip']
282 && $GLOBALS['cfgRelation']['commwork']) {
283 $db_tooltips = PMA_getDbComments();
286 if (!$GLOBALS['cfg']['LeftFrameDBTree']) {
287 $separators = array();
288 } elseif (is_array($GLOBALS['cfg']['LeftFrameDBSeparator'])) {
289 $separators = $GLOBALS['cfg']['LeftFrameDBSeparator'];
290 } elseif (!empty($GLOBALS['cfg']['LeftFrameDBSeparator'])) {
291 $separators = array($GLOBALS['cfg']['LeftFrameDBSeparator']);
292 } else {
293 $separators = array();
296 foreach ($this->getLimitedItems($offset, $count) as $key => $db) {
297 // Get comments from PMA comments table
298 $db_tooltip = '';
300 if (isset($db_tooltips[$db])) {
301 $db_tooltip = $db_tooltips[$db];
304 $pos = false;
306 foreach($separators as $separator) {
307 // use strpos instead of strrpos; it seems more common to
308 // have the db name, the separator, then the rest which
309 // might contain a separator
310 // like dbname_the_rest
311 $pos = strpos($db, $separator, 1);
313 if ($pos !== false) {
314 break;
318 if ($pos !== false) {
319 $group = substr($db, 0, $pos);
320 $disp_name_cut = substr($db, $pos);
321 } else {
322 $group = $db;
323 $disp_name_cut = $db;
326 $disp_name = $db;
327 if ($db_tooltip && $GLOBALS['cfg']['ShowTooltipAliasDB']) {
328 $disp_name = $db_tooltip;
329 $disp_name_cut = $db_tooltip;
330 $db_tooltip = $db;
333 $dbgroups[$group][$db] = array(
334 'name' => $db,
335 'disp_name_cut' => $disp_name_cut,
336 'disp_name' => $disp_name,
337 'comment' => $db_tooltip,
340 if ($GLOBALS['cfg']['Server']['CountTables']) {
341 $dbgroups[$group][$db]['num_tables'] = PMA_getTableCount($db);
343 } // end foreach ($GLOBALS['PMA_List_Database']->items as $db)
344 return $dbgroups;
348 * returns a part of the items
350 * @uses array_slice()
351 * @param integer $offset
352 * @param integer $count
353 * @return array some items
355 public function getLimitedItems($offset, $count)
357 return array_slice($this->getArrayCopy(), $offset, $count);
361 * returns html code for list with dbs
363 * @return string html code list
365 public function getHtmlListGrouped($selected = '', $offset, $count)
367 if (true === $selected) {
368 $selected = $this->getDefault();
371 $return = '<ul id="databaseList" xml:lang="en" dir="ltr">' . "\n";
372 foreach ($this->getGroupedDetails($offset, $count) as $group => $dbs) {
373 if (count($dbs) > 1) {
374 $return .= '<li><span>' . htmlspecialchars($group) . '</span><ul>' . "\n";
375 // whether display db_name cut by the group part
376 $cut = true;
377 } else {
378 // .. or full
379 $cut = false;
381 foreach ($dbs as $db) {
382 $return .= '<li';
383 if ($db['name'] == $selected) {
384 $return .= ' class="selected"';
386 $return .= '><a';
387 if (! empty($db['comment'])) {
388 $return .= ' title="' . htmlspecialchars($db['comment']) . '"';
390 $return .= ' href="index.php?' . PMA_generate_common_url($db['name'])
391 . '" target="_parent">';
392 if ($cut) {
393 $return .= htmlspecialchars($db['disp_name_cut']);
394 } else {
395 $return .= htmlspecialchars($db['disp_name']);
398 if (! empty($db['num_tables'])) {
399 $return .= ' (' . $db['num_tables'] . ')';
401 $return .= '</a></li>' . "\n";
403 if (count($dbs) > 1) {
404 $return .= '</ul></li>' . "\n";
407 $return .= '</ul>';
409 return $return;
413 * returns html code for select form element with dbs
415 * @todo IE can not handle different text directions in select boxes so,
416 * as mostly names will be in english, we set the whole selectbox to LTR
417 * and EN
419 * @return string html code select
421 public function getHtmlSelectGrouped($selected = '', $offset, $count)
423 if (true === $selected) {
424 $selected = $this->getDefault();
427 $return = '<select name="db" id="lightm_db" xml:lang="en" dir="ltr"'
428 . ' onchange="if (this.value != \'\') window.parent.openDb(this.value);">' . "\n"
429 . '<option value="" dir="' . $GLOBALS['text_dir'] . '">'
430 . '(' . __('Databases') . ') ...</option>' . "\n";
431 foreach ($this->getGroupedDetails($offset, $count) as $group => $dbs) {
432 if (count($dbs) > 1) {
433 $return .= '<optgroup label="' . htmlspecialchars($group)
434 . '">' . "\n";
435 // whether display db_name cuted by the group part
436 $cut = true;
437 } else {
438 // .. or full
439 $cut = false;
441 foreach ($dbs as $db) {
442 $return .= '<option value="' . htmlspecialchars($db['name']) . '"'
443 .' title="' . htmlspecialchars($db['comment']) . '"';
444 if ($db['name'] == $selected) {
445 $return .= ' selected="selected"';
447 $return .= '>' . htmlspecialchars($cut ? $db['disp_name_cut'] : $db['disp_name']);
448 if (! empty($db['num_tables'])) {
449 $return .= ' (' . $db['num_tables'] . ')';
451 $return .= '</option>' . "\n";
453 if (count($dbs) > 1) {
454 $return .= '</optgroup>' . "\n";
457 $return .= '</select>';
459 return $return;
463 * this is just a backup, if all is fine this can be deleted later
465 * @deprecated
467 protected function _checkAgainstPrivTables()
469 // 1. get allowed dbs from the "mysql.db" table
470 // User can be blank (anonymous user)
471 $local_query = "
472 SELECT DISTINCT `Db` FROM `mysql`.`db`
473 WHERE `Select_priv` = 'Y'
474 AND `User`
475 IN ('" . PMA_sqlAddslashes($GLOBALS['cfg']['Server']['user']) . "', '')";
476 $tmp_mydbs = PMA_DBI_fetch_result($local_query, null, null,
477 $GLOBALS['controllink']);
478 if ($tmp_mydbs) {
479 // Will use as associative array of the following 2 code
480 // lines:
481 // the 1st is the only line intact from before
482 // correction,
483 // the 2nd replaces $dblist[] = $row['Db'];
485 // Code following those 2 lines in correction continues
486 // populating $dblist[], as previous code did. But it is
487 // now populated with actual database names instead of
488 // with regular expressions.
489 $tmp_alldbs = PMA_DBI_query('SHOW DATABASES;', $GLOBALS['controllink']);
490 // all databases cases - part 2
491 if (isset($tmp_mydbs['%'])) {
492 while ($tmp_row = PMA_DBI_fetch_row($tmp_alldbs)) {
493 $dblist[] = $tmp_row[0];
494 } // end while
495 } else {
496 while ($tmp_row = PMA_DBI_fetch_row($tmp_alldbs)) {
497 $tmp_db = $tmp_row[0];
498 if (isset($tmp_mydbs[$tmp_db]) && $tmp_mydbs[$tmp_db] == 1) {
499 $dblist[] = $tmp_db;
500 $tmp_mydbs[$tmp_db] = 0;
501 } elseif (!isset($dblist[$tmp_db])) {
502 foreach ($tmp_mydbs as $tmp_matchpattern => $tmp_value) {
503 // fixed bad regexp
504 // TODO: db names may contain characters
505 // that are regexp instructions
506 $re = '(^|(\\\\\\\\)+|[^\])';
507 $tmp_regex = preg_replace('/' . addcslashes($re,'/') . '%/', '\\1.*', preg_replace('/' . addcslashes($re,'/') . '_/', '\\1.{1}', $tmp_matchpattern));
508 // Fixed db name matching
509 // 2000-08-28 -- Benjamin Gandon
510 if (preg_match('/^' . addcslashes($tmp_regex,'/') . '$/', $tmp_db)) {
511 $dblist[] = $tmp_db;
512 break;
514 } // end while
515 } // end if ... elseif ...
516 } // end while
517 } // end else
518 PMA_DBI_free_result($tmp_alldbs);
519 unset($tmp_mydbs);
520 } // end if
522 // 2. get allowed dbs from the "mysql.tables_priv" table
523 $local_query = 'SELECT DISTINCT Db FROM mysql.tables_priv WHERE Table_priv LIKE \'%Select%\' AND User = \'' . PMA_sqlAddslashes($GLOBALS['cfg']['Server']['user']) . '\'';
524 $rs = PMA_DBI_try_query($local_query, $GLOBALS['controllink']);
525 if ($rs && @PMA_DBI_num_rows($rs)) {
526 while ($row = PMA_DBI_fetch_assoc($rs)) {
527 if (!in_array($row['Db'], $dblist)) {
528 $dblist[] = $row['Db'];
530 } // end while
531 PMA_DBI_free_result($rs);
532 } // end if