PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/libraries/classes/Controllers/Server/DatabasesController.php

http://github.com/phpmyadmin/phpmyadmin
PHP | 342 lines | 263 code | 45 blank | 34 comment | 20 complexity | f8ddb0bac6d43d7a886842a6a547471d MD5 | raw file
Possible License(s): GPL-2.0, MIT, LGPL-3.0
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Controllers\Server;
  4. use PhpMyAdmin\Charsets;
  5. use PhpMyAdmin\Charsets\Charset;
  6. use PhpMyAdmin\Charsets\Collation;
  7. use PhpMyAdmin\CheckUserPrivileges;
  8. use PhpMyAdmin\Controllers\AbstractController;
  9. use PhpMyAdmin\DatabaseInterface;
  10. use PhpMyAdmin\Query\Utilities;
  11. use PhpMyAdmin\RelationCleanup;
  12. use PhpMyAdmin\ReplicationInfo;
  13. use PhpMyAdmin\ResponseRenderer;
  14. use PhpMyAdmin\Template;
  15. use PhpMyAdmin\Transformations;
  16. use PhpMyAdmin\Url;
  17. use PhpMyAdmin\Util;
  18. use function __;
  19. use function array_keys;
  20. use function array_search;
  21. use function count;
  22. use function in_array;
  23. use function mb_strtolower;
  24. use function str_contains;
  25. use function strlen;
  26. /**
  27. * Handles viewing and creating and deleting databases
  28. */
  29. class DatabasesController extends AbstractController
  30. {
  31. /** @var array array of database details */
  32. private $databases = [];
  33. /** @var int number of databases */
  34. private $databaseCount = 0;
  35. /** @var string sort by column */
  36. private $sortBy;
  37. /** @var string sort order of databases */
  38. private $sortOrder;
  39. /** @var bool whether to show database statistics */
  40. private $hasStatistics;
  41. /** @var int position in list navigation */
  42. private $position;
  43. /** @var Transformations */
  44. private $transformations;
  45. /** @var RelationCleanup */
  46. private $relationCleanup;
  47. /** @var DatabaseInterface */
  48. private $dbi;
  49. public function __construct(
  50. ResponseRenderer $response,
  51. Template $template,
  52. Transformations $transformations,
  53. RelationCleanup $relationCleanup,
  54. DatabaseInterface $dbi
  55. ) {
  56. parent::__construct($response, $template);
  57. $this->transformations = $transformations;
  58. $this->relationCleanup = $relationCleanup;
  59. $this->dbi = $dbi;
  60. $checkUserPrivileges = new CheckUserPrivileges($dbi);
  61. $checkUserPrivileges->getPrivileges();
  62. }
  63. public function __invoke(): void
  64. {
  65. global $cfg, $server, $dblist, $is_create_db_priv;
  66. global $db_to_create, $text_dir, $errorUrl;
  67. $params = [
  68. 'statistics' => $_REQUEST['statistics'] ?? null,
  69. 'pos' => $_REQUEST['pos'] ?? null,
  70. 'sort_by' => $_REQUEST['sort_by'] ?? null,
  71. 'sort_order' => $_REQUEST['sort_order'] ?? null,
  72. ];
  73. $this->addScriptFiles(['server/databases.js']);
  74. $errorUrl = Url::getFromRoute('/');
  75. if ($this->dbi->isSuperUser()) {
  76. $this->dbi->selectDb('mysql');
  77. }
  78. $replicationInfo = new ReplicationInfo($this->dbi);
  79. $replicationInfo->load($_POST['master_connection'] ?? null);
  80. $primaryInfo = $replicationInfo->getPrimaryInfo();
  81. $replicaInfo = $replicationInfo->getReplicaInfo();
  82. $this->setSortDetails($params['sort_by'], $params['sort_order']);
  83. $this->hasStatistics = ! empty($params['statistics']);
  84. $this->position = ! empty($params['pos']) ? (int) $params['pos'] : 0;
  85. /**
  86. * Gets the databases list
  87. */
  88. if ($server > 0) {
  89. $this->databases = $this->dbi->getDatabasesFull(
  90. null,
  91. $this->hasStatistics,
  92. DatabaseInterface::CONNECT_USER,
  93. $this->sortBy,
  94. $this->sortOrder,
  95. $this->position,
  96. true
  97. );
  98. $this->databaseCount = count($dblist->databases);
  99. }
  100. $urlParams = [
  101. 'statistics' => $this->hasStatistics,
  102. 'pos' => $this->position,
  103. 'sort_by' => $this->sortBy,
  104. 'sort_order' => $this->sortOrder,
  105. ];
  106. $databases = $this->getDatabases($primaryInfo, $replicaInfo);
  107. $charsetsList = [];
  108. if ($cfg['ShowCreateDb'] && $is_create_db_priv) {
  109. $charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
  110. $collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
  111. $serverCollation = $this->dbi->getServerCollation();
  112. /** @var Charset $charset */
  113. foreach ($charsets as $charset) {
  114. $collationsList = [];
  115. /** @var Collation $collation */
  116. foreach ($collations[$charset->getName()] as $collation) {
  117. $collationsList[] = [
  118. 'name' => $collation->getName(),
  119. 'description' => $collation->getDescription(),
  120. 'is_selected' => $serverCollation === $collation->getName(),
  121. ];
  122. }
  123. $charsetsList[] = [
  124. 'name' => $charset->getName(),
  125. 'description' => $charset->getDescription(),
  126. 'collations' => $collationsList,
  127. ];
  128. }
  129. }
  130. $headerStatistics = $this->getStatisticsColumns();
  131. $this->render('server/databases/index', [
  132. 'is_create_database_shown' => $cfg['ShowCreateDb'],
  133. 'has_create_database_privileges' => $is_create_db_priv,
  134. 'has_statistics' => $this->hasStatistics,
  135. 'database_to_create' => $db_to_create,
  136. 'databases' => $databases['databases'],
  137. 'total_statistics' => $databases['total_statistics'],
  138. 'header_statistics' => $headerStatistics,
  139. 'charsets' => $charsetsList,
  140. 'database_count' => $this->databaseCount,
  141. 'pos' => $this->position,
  142. 'url_params' => $urlParams,
  143. 'max_db_list' => $cfg['MaxDbList'],
  144. 'has_master_replication' => $primaryInfo['status'],
  145. 'has_slave_replication' => $replicaInfo['status'],
  146. 'is_drop_allowed' => $this->dbi->isSuperUser() || $cfg['AllowUserDropDatabase'],
  147. 'text_dir' => $text_dir,
  148. ]);
  149. }
  150. /**
  151. * Extracts parameters sort order and sort by
  152. *
  153. * @param string|null $sortBy sort by
  154. * @param string|null $sortOrder sort order
  155. */
  156. private function setSortDetails(?string $sortBy, ?string $sortOrder): void
  157. {
  158. if (empty($sortBy)) {
  159. $this->sortBy = 'SCHEMA_NAME';
  160. } else {
  161. $sortByAllowList = [
  162. 'SCHEMA_NAME',
  163. 'DEFAULT_COLLATION_NAME',
  164. 'SCHEMA_TABLES',
  165. 'SCHEMA_TABLE_ROWS',
  166. 'SCHEMA_DATA_LENGTH',
  167. 'SCHEMA_INDEX_LENGTH',
  168. 'SCHEMA_LENGTH',
  169. 'SCHEMA_DATA_FREE',
  170. ];
  171. $this->sortBy = 'SCHEMA_NAME';
  172. if (in_array($sortBy, $sortByAllowList)) {
  173. $this->sortBy = $sortBy;
  174. }
  175. }
  176. $this->sortOrder = 'asc';
  177. if (! isset($sortOrder) || mb_strtolower($sortOrder) !== 'desc') {
  178. return;
  179. }
  180. $this->sortOrder = 'desc';
  181. }
  182. /**
  183. * @param array $primaryInfo
  184. * @param array $replicaInfo
  185. *
  186. * @return array
  187. */
  188. private function getDatabases($primaryInfo, $replicaInfo): array
  189. {
  190. global $cfg;
  191. $databases = [];
  192. $totalStatistics = $this->getStatisticsColumns();
  193. foreach ($this->databases as $database) {
  194. $replication = [
  195. 'master' => ['status' => $primaryInfo['status']],
  196. 'slave' => ['status' => $replicaInfo['status']],
  197. ];
  198. if ($primaryInfo['status']) {
  199. $key = array_search($database['SCHEMA_NAME'], $primaryInfo['Ignore_DB']);
  200. $replication['master']['is_replicated'] = false;
  201. if (strlen((string) $key) === 0) {
  202. $key = array_search($database['SCHEMA_NAME'], $primaryInfo['Do_DB']);
  203. if (strlen((string) $key) > 0 || count($primaryInfo['Do_DB']) === 0) {
  204. $replication['master']['is_replicated'] = true;
  205. }
  206. }
  207. }
  208. if ($replicaInfo['status']) {
  209. $key = array_search($database['SCHEMA_NAME'], $replicaInfo['Ignore_DB']);
  210. $replication['slave']['is_replicated'] = false;
  211. if (strlen((string) $key) === 0) {
  212. $key = array_search($database['SCHEMA_NAME'], $replicaInfo['Do_DB']);
  213. if (strlen((string) $key) > 0 || count($replicaInfo['Do_DB']) === 0) {
  214. $replication['slave']['is_replicated'] = true;
  215. }
  216. }
  217. }
  218. $statistics = $this->getStatisticsColumns();
  219. if ($this->hasStatistics) {
  220. foreach (array_keys($statistics) as $key) {
  221. $statistics[$key]['raw'] = $database[$key] ?? null;
  222. $totalStatistics[$key]['raw'] += (int) $database[$key] ?? 0;
  223. }
  224. }
  225. $url = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
  226. $url .= Url::getCommonRaw(
  227. ['db' => $database['SCHEMA_NAME']],
  228. ! str_contains($url, '?') ? '?' : '&'
  229. );
  230. $databases[$database['SCHEMA_NAME']] = [
  231. 'name' => $database['SCHEMA_NAME'],
  232. 'collation' => [],
  233. 'statistics' => $statistics,
  234. 'replication' => $replication,
  235. 'is_system_schema' => Utilities::isSystemSchema($database['SCHEMA_NAME'], true),
  236. 'is_pmadb' => $database['SCHEMA_NAME'] === ($cfg['Server']['pmadb'] ?? ''),
  237. 'url' => $url,
  238. ];
  239. $collation = Charsets::findCollationByName(
  240. $this->dbi,
  241. $cfg['Server']['DisableIS'],
  242. $database['DEFAULT_COLLATION_NAME']
  243. );
  244. if ($collation === null) {
  245. continue;
  246. }
  247. $databases[$database['SCHEMA_NAME']]['collation'] = [
  248. 'name' => $collation->getName(),
  249. 'description' => $collation->getDescription(),
  250. ];
  251. }
  252. return [
  253. 'databases' => $databases,
  254. 'total_statistics' => $totalStatistics,
  255. ];
  256. }
  257. /**
  258. * Prepares the statistics columns
  259. *
  260. * @return array
  261. */
  262. private function getStatisticsColumns(): array
  263. {
  264. return [
  265. 'SCHEMA_TABLES' => [
  266. 'title' => __('Tables'),
  267. 'format' => 'number',
  268. 'raw' => 0,
  269. ],
  270. 'SCHEMA_TABLE_ROWS' => [
  271. 'title' => __('Rows'),
  272. 'format' => 'number',
  273. 'raw' => 0,
  274. ],
  275. 'SCHEMA_DATA_LENGTH' => [
  276. 'title' => __('Data'),
  277. 'format' => 'byte',
  278. 'raw' => 0,
  279. ],
  280. 'SCHEMA_INDEX_LENGTH' => [
  281. 'title' => __('Indexes'),
  282. 'format' => 'byte',
  283. 'raw' => 0,
  284. ],
  285. 'SCHEMA_LENGTH' => [
  286. 'title' => __('Total'),
  287. 'format' => 'byte',
  288. 'raw' => 0,
  289. ],
  290. 'SCHEMA_DATA_FREE' => [
  291. 'title' => __('Overhead'),
  292. 'format' => 'byte',
  293. 'raw' => 0,
  294. ],
  295. ];
  296. }
  297. }