diff options
author | Maurício Meneghini Fauth <mauricio@fauth.dev> | 2021-09-08 03:19:43 +0300 |
---|---|---|
committer | Maurício Meneghini Fauth <mauricio@fauth.dev> | 2021-09-08 03:19:43 +0300 |
commit | e3554879ef7a4879446db25669563df004f832a2 (patch) | |
tree | 6a5788e42622d01918d81971e93b178dd09a6831 | |
parent | a73e26c81f0ed01fe717e476ab3c7937efa702e4 (diff) |
Extract actions from controllers to new controllers
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
35 files changed, 2667 insertions, 1447 deletions
diff --git a/libraries/classes/Controllers/Database/Operations/CollationController.php b/libraries/classes/Controllers/Database/Operations/CollationController.php new file mode 100644 index 0000000000..212e893324 --- /dev/null +++ b/libraries/classes/Controllers/Database/Operations/CollationController.php @@ -0,0 +1,112 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Operations; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\Operations; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Url; +use PhpMyAdmin\Util; + +use function __; + +final class CollationController extends AbstractController +{ + /** @var Operations */ + private $operations; + + /** @var DatabaseInterface */ + private $dbi; + + /** + * @param ResponseRenderer $response + * @param string $db Database name + * @param DatabaseInterface $dbi + */ + public function __construct( + $response, + Template $template, + $db, + Operations $operations, + $dbi + ) { + parent::__construct($response, $template, $db); + $this->operations = $operations; + $this->dbi = $dbi; + } + + public function __invoke(): void + { + global $db, $cfg, $errorUrl; + + if (! $this->response->isAjax()) { + return; + } + + if (empty($_POST['db_collation'])) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', Message::error(__('No collation provided.'))); + + return; + } + + Util::checkParameters(['db']); + + $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); + $errorUrl .= Url::getCommon(['db' => $db], '&'); + + if (! $this->hasDatabase()) { + return; + } + + $sql_query = 'ALTER DATABASE ' . Util::backquote($db) + . ' DEFAULT' . Util::getCharsetQueryPart($_POST['db_collation'] ?? ''); + $this->dbi->query($sql_query); + $message = Message::success(); + + /** + * Changes tables charset if requested by the user + */ + if ( + isset($_POST['change_all_tables_collations']) && + $_POST['change_all_tables_collations'] === 'on' + ) { + [$tables] = Util::getDbInfo($db, null); + foreach ($tables as $tableName => $data) { + if ($this->dbi->getTable($db, $tableName)->isView()) { + // Skip views, we can not change the collation of a view. + // issue #15283 + continue; + } + + $sql_query = 'ALTER TABLE ' + . Util::backquote($db) + . '.' + . Util::backquote($tableName) + . ' DEFAULT ' + . Util::getCharsetQueryPart($_POST['db_collation'] ?? ''); + $this->dbi->query($sql_query); + + /** + * Changes columns charset if requested by the user + */ + if ( + ! isset($_POST['change_all_tables_columns_collations']) || + $_POST['change_all_tables_columns_collations'] !== 'on' + ) { + continue; + } + + $this->operations->changeAllColumnsCollation($db, $tableName, $_POST['db_collation']); + } + } + + $this->response->setRequestStatus($message->isSuccess()); + $this->response->addJSON('message', $message); + } +} diff --git a/libraries/classes/Controllers/Database/OperationsController.php b/libraries/classes/Controllers/Database/OperationsController.php index 3d9566a860..17f148293c 100644 --- a/libraries/classes/Controllers/Database/OperationsController.php +++ b/libraries/classes/Controllers/Database/OperationsController.php @@ -68,7 +68,7 @@ class OperationsController extends AbstractController $this->dbi = $dbi; } - public function index(): void + public function __invoke(): void { global $cfg, $db, $server, $sql_query, $move, $message, $tables_full, $errorUrl; global $export_sql_plugin, $views, $sqlConstratints, $local_query, $reload, $urlParams, $tables; @@ -354,74 +354,4 @@ class OperationsController extends AbstractController 'collations' => $collations, ]); } - - public function collation(): void - { - global $db, $cfg, $errorUrl; - - if (! $this->response->isAjax()) { - return; - } - - if (empty($_POST['db_collation'])) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', Message::error(__('No collation provided.'))); - - return; - } - - Util::checkParameters(['db']); - - $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); - $errorUrl .= Url::getCommon(['db' => $db], '&'); - - if (! $this->hasDatabase()) { - return; - } - - $sql_query = 'ALTER DATABASE ' . Util::backquote($db) - . ' DEFAULT' . Util::getCharsetQueryPart($_POST['db_collation'] ?? ''); - $this->dbi->query($sql_query); - $message = Message::success(); - - /** - * Changes tables charset if requested by the user - */ - if ( - isset($_POST['change_all_tables_collations']) && - $_POST['change_all_tables_collations'] === 'on' - ) { - [$tables] = Util::getDbInfo($db, null); - foreach ($tables as $tableName => $data) { - if ($this->dbi->getTable($db, $tableName)->isView()) { - // Skip views, we can not change the collation of a view. - // issue #15283 - continue; - } - - $sql_query = 'ALTER TABLE ' - . Util::backquote($db) - . '.' - . Util::backquote($tableName) - . ' DEFAULT ' - . Util::getCharsetQueryPart($_POST['db_collation'] ?? ''); - $this->dbi->query($sql_query); - - /** - * Changes columns charset if requested by the user - */ - if ( - ! isset($_POST['change_all_tables_columns_collations']) || - $_POST['change_all_tables_columns_collations'] !== 'on' - ) { - continue; - } - - $this->operations->changeAllColumnsCollation($db, $tableName, $_POST['db_collation']); - } - } - - $this->response->setRequestStatus($message->isSuccess()); - $this->response->addJSON('message', $message); - } } diff --git a/libraries/classes/Controllers/Database/Structure/AddPrefixController.php b/libraries/classes/Controllers/Database/Structure/AddPrefixController.php new file mode 100644 index 0000000000..7a7cfadc29 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/AddPrefixController.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; + +use function __; + +final class AddPrefixController extends AbstractController +{ + public function __invoke(): void + { + global $db; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $params = ['db' => $db]; + foreach ($selected as $selectedValue) { + $params['selected'][] = $selectedValue; + } + + $this->response->disable(); + $this->render('database/structure/add_prefix', ['url_params' => $params]); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/AddPrefixTableController.php b/libraries/classes/Controllers/Database/Structure/AddPrefixTableController.php new file mode 100644 index 0000000000..711a562d6d --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/AddPrefixTableController.php @@ -0,0 +1,64 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Util; + +use function count; + +final class AddPrefixTableController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi, StructureController $structureController) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $message, $sql_query; + + $selected = $_POST['selected'] ?? []; + + $sql_query = ''; + $selectedCount = count($selected); + + for ($i = 0; $i < $selectedCount; $i++) { + $newTableName = $_POST['add_prefix'] . $selected[$i]; + $aQuery = 'ALTER TABLE ' . Util::backquote($selected[$i]) + . ' RENAME ' . Util::backquote($newTableName); + + $sql_query .= $aQuery . ';' . "\n"; + $this->dbi->selectDb($db); + $this->dbi->query($aQuery); + } + + $message = Message::success(); + + if (empty($_POST['message'])) { + $_POST['message'] = $message; + } + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/CentralColumns/AddController.php b/libraries/classes/Controllers/Database/Structure/CentralColumns/AddController.php new file mode 100644 index 0000000000..cb10f368c6 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/CentralColumns/AddController.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure\CentralColumns; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\Database\CentralColumns; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; + +use function __; + +final class AddController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi, StructureController $structureController) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $message; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $centralColumns = new CentralColumns($this->dbi); + $error = $centralColumns->syncUniqueColumns($selected); + + $message = $error instanceof Message ? $error : Message::success(__('Success!')); + + unset($_POST['submit_mult']); + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/CentralColumns/MakeConsistentController.php b/libraries/classes/Controllers/Database/Structure/CentralColumns/MakeConsistentController.php new file mode 100644 index 0000000000..a0dfe49e70 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/CentralColumns/MakeConsistentController.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure\CentralColumns; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\Database\CentralColumns; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; + +use function __; + +final class MakeConsistentController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi, StructureController $structureController) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $message; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $centralColumns = new CentralColumns($this->dbi); + $error = $centralColumns->makeConsistentWithList($db, $selected); + + $message = $error instanceof Message ? $error : Message::success(__('Success!')); + + unset($_POST['submit_mult']); + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/CentralColumns/RemoveController.php b/libraries/classes/Controllers/Database/Structure/CentralColumns/RemoveController.php new file mode 100644 index 0000000000..c58785214c --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/CentralColumns/RemoveController.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure\CentralColumns; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\Database\CentralColumns; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; + +use function __; + +final class RemoveController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi, StructureController $structureController) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $message; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $centralColumns = new CentralColumns($this->dbi); + $error = $centralColumns->deleteColumnsFromList($_POST['db'], $selected); + + $message = $error instanceof Message ? $error : Message::success(__('Success!')); + + unset($_POST['submit_mult']); + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/ChangePrefixFormController.php b/libraries/classes/Controllers/Database/Structure/ChangePrefixFormController.php new file mode 100644 index 0000000000..6529585125 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/ChangePrefixFormController.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; + +use function __; + +final class ChangePrefixFormController extends AbstractController +{ + public function __invoke(): void + { + global $db; + + $selected = $_POST['selected_tbl'] ?? []; + $submitMult = $_POST['submit_mult'] ?? ''; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $route = '/database/structure/replace-prefix'; + if ($submitMult === 'copy_tbl_change_prefix') { + $route = '/database/structure/copy-table-with-prefix'; + } + + $urlParams = ['db' => $db]; + foreach ($selected as $selectedValue) { + $urlParams['selected'][] = $selectedValue; + } + + $this->response->disable(); + $this->render('database/structure/change_prefix_form', [ + 'route' => $route, + 'url_params' => $urlParams, + ]); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/CopyFormController.php b/libraries/classes/Controllers/Database/Structure/CopyFormController.php new file mode 100644 index 0000000000..70ba831009 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/CopyFormController.php @@ -0,0 +1,45 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; + +use function __; + +final class CopyFormController extends AbstractController +{ + public function __invoke(): void + { + global $db, $dblist; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $urlParams = ['db' => $db]; + foreach ($selected as $selectedValue) { + $urlParams['selected'][] = $selectedValue; + } + + $databasesList = $dblist->databases; + foreach ($databasesList as $key => $databaseName) { + if ($databaseName == $db) { + $databasesList->offsetUnset($key); + break; + } + } + + $this->response->disable(); + $this->render('database/structure/copy_form', [ + 'url_params' => $urlParams, + 'options' => $databasesList->getList(), + ]); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/CopyTableController.php b/libraries/classes/Controllers/Database/Structure/CopyTableController.php new file mode 100644 index 0000000000..7af069693c --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/CopyTableController.php @@ -0,0 +1,81 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\Message; +use PhpMyAdmin\Operations; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Table; +use PhpMyAdmin\Template; + +use function count; + +final class CopyTableController extends AbstractController +{ + /** @var Operations */ + private $operations; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + */ + public function __construct( + $response, + Template $template, + $db, + Operations $operations, + StructureController $structureController + ) { + parent::__construct($response, $template, $db); + $this->operations = $operations; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $message; + + $selected = $_POST['selected'] ?? []; + $targetDb = $_POST['target_db'] ?? null; + $selectedCount = count($selected); + + for ($i = 0; $i < $selectedCount; $i++) { + Table::moveCopy( + $db, + $selected[$i], + $targetDb, + $selected[$i], + $_POST['what'], + false, + 'one_table', + isset($_POST['drop_if_exists']) && $_POST['drop_if_exists'] === 'true' + ); + + if (empty($_POST['adjust_privileges'])) { + continue; + } + + $this->operations->adjustPrivilegesCopyTable( + $db, + $selected[$i], + $targetDb, + $selected[$i] + ); + } + + $message = Message::success(); + + if (empty($_POST['message'])) { + $_POST['message'] = $message; + } + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/CopyTableWithPrefixController.php b/libraries/classes/Controllers/Database/Structure/CopyTableWithPrefixController.php new file mode 100644 index 0000000000..de07cf8f4b --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/CopyTableWithPrefixController.php @@ -0,0 +1,67 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Table; +use PhpMyAdmin\Template; + +use function count; +use function mb_strlen; +use function mb_substr; + +final class CopyTableWithPrefixController extends AbstractController +{ + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + */ + public function __construct($response, Template $template, $db, StructureController $structureController) + { + parent::__construct($response, $template, $db); + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $message; + + $selected = $_POST['selected'] ?? []; + $fromPrefix = $_POST['from_prefix'] ?? null; + $toPrefix = $_POST['to_prefix'] ?? null; + + $selectedCount = count($selected); + + for ($i = 0; $i < $selectedCount; $i++) { + $current = $selected[$i]; + $newTableName = $toPrefix . mb_substr($current, mb_strlen((string) $fromPrefix)); + + Table::moveCopy( + $db, + $current, + $db, + $newTableName, + 'data', + false, + 'one_table', + isset($_POST['drop_if_exists']) && $_POST['drop_if_exists'] === 'true' + ); + } + + $message = Message::success(); + + if (empty($_POST['message'])) { + $_POST['message'] = $message; + } + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/DropFormController.php b/libraries/classes/Controllers/Database/Structure/DropFormController.php new file mode 100644 index 0000000000..91793c1373 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/DropFormController.php @@ -0,0 +1,86 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Util; +use PhpMyAdmin\Utils\ForeignKey; + +use function __; +use function htmlspecialchars; +use function in_array; + +final class DropFormController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + } + + public function __invoke(): void + { + global $db; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $views = $this->dbi->getVirtualTables($db); + + $fullQueryViews = ''; + $fullQuery = ''; + + foreach ($selected as $selectedValue) { + $current = $selectedValue; + if (! empty($views) && in_array($current, $views)) { + $fullQueryViews .= (empty($fullQueryViews) ? 'DROP VIEW ' : ', ') + . Util::backquote(htmlspecialchars($current)); + } else { + $fullQuery .= (empty($fullQuery) ? 'DROP TABLE ' : ', ') + . Util::backquote(htmlspecialchars($current)); + } + } + + if (! empty($fullQuery)) { + $fullQuery .= ';<br>' . "\n"; + } + + if (! empty($fullQueryViews)) { + $fullQuery .= $fullQueryViews . ';<br>' . "\n"; + } + + $urlParams = ['db' => $db]; + foreach ($selected as $selectedValue) { + $urlParams['selected'][] = $selectedValue; + } + + foreach ($views as $current) { + $urlParams['views'][] = $current; + } + + $this->render('database/structure/drop_form', [ + 'url_params' => $urlParams, + 'full_query' => $fullQuery, + 'is_foreign_key_check' => ForeignKey::isCheckEnabled(), + ]); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/DropTableController.php b/libraries/classes/Controllers/Database/Structure/DropTableController.php new file mode 100644 index 0000000000..113a918eed --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/DropTableController.php @@ -0,0 +1,136 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\RelationCleanup; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Util; +use PhpMyAdmin\Utils\ForeignKey; + +use function __; +use function count; +use function in_array; + +final class DropTableController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var RelationCleanup */ + private $relationCleanup; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct( + $response, + Template $template, + $db, + $dbi, + RelationCleanup $relationCleanup, + StructureController $structureController + ) { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->relationCleanup = $relationCleanup; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $message, $reload, $sql_query; + + $reload = $_POST['reload'] ?? $reload ?? null; + $multBtn = $_POST['mult_btn'] ?? ''; + $selected = $_POST['selected'] ?? []; + + $views = $this->dbi->getVirtualTables($db); + + if ($multBtn !== __('Yes')) { + $message = Message::success(__('No change')); + + if (empty($_POST['message'])) { + $_POST['message'] = Message::success(); + } + + unset($_POST['mult_btn']); + + ($this->structureController)(); + + return; + } + + $defaultFkCheckValue = ForeignKey::handleDisableCheckInit(); + $sql_query = ''; + $sqlQueryViews = ''; + $selectedCount = count($selected); + + for ($i = 0; $i < $selectedCount; $i++) { + $this->relationCleanup->table($db, $selected[$i]); + $current = $selected[$i]; + + if (! empty($views) && in_array($current, $views)) { + $sqlQueryViews .= (empty($sqlQueryViews) ? 'DROP VIEW ' : ', ') . Util::backquote($current); + } else { + $sql_query .= (empty($sql_query) ? 'DROP TABLE ' : ', ') . Util::backquote($current); + } + + $reload = 1; + } + + if (! empty($sql_query)) { + $sql_query .= ';'; + } elseif (! empty($sqlQueryViews)) { + $sql_query = $sqlQueryViews . ';'; + unset($sqlQueryViews); + } + + // Unset cache values for tables count, issue #14205 + if (isset($_SESSION['tmpval'])) { + if (isset($_SESSION['tmpval']['table_limit_offset'])) { + unset($_SESSION['tmpval']['table_limit_offset']); + } + + if (isset($_SESSION['tmpval']['table_limit_offset_db'])) { + unset($_SESSION['tmpval']['table_limit_offset_db']); + } + } + + $this->dbi->selectDb($db); + $result = $this->dbi->tryQuery($sql_query); + + if ($result && ! empty($sqlQueryViews)) { + $sql_query .= ' ' . $sqlQueryViews . ';'; + $result = $this->dbi->tryQuery($sqlQueryViews); + unset($sqlQueryViews); + } + + if (! $result) { + $message = Message::error((string) $this->dbi->getError()); + } + + ForeignKey::handleDisableCheckCleanup($defaultFkCheckValue); + + $message = Message::success(); + + if (empty($_POST['message'])) { + $_POST['message'] = $message; + } + + unset($_POST['mult_btn']); + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/EmptyFormController.php b/libraries/classes/Controllers/Database/Structure/EmptyFormController.php new file mode 100644 index 0000000000..556d64493b --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/EmptyFormController.php @@ -0,0 +1,44 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Util; +use PhpMyAdmin\Utils\ForeignKey; + +use function __; +use function htmlspecialchars; + +final class EmptyFormController extends AbstractController +{ + public function __invoke(): void + { + global $db; + + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $fullQuery = ''; + $urlParams = ['db' => $db]; + + foreach ($selected as $selectedValue) { + $fullQuery .= 'TRUNCATE '; + $fullQuery .= Util::backquote(htmlspecialchars($selectedValue)) . ';<br>'; + $urlParams['selected'][] = $selectedValue; + } + + $this->render('database/structure/empty_form', [ + 'url_params' => $urlParams, + 'full_query' => $fullQuery, + 'is_foreign_key_check' => ForeignKey::isCheckEnabled(), + ]); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/EmptyTableController.php b/libraries/classes/Controllers/Database/Structure/EmptyTableController.php new file mode 100644 index 0000000000..c543eb0090 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/EmptyTableController.php @@ -0,0 +1,123 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FlashMessages; +use PhpMyAdmin\Message; +use PhpMyAdmin\Operations; +use PhpMyAdmin\Relation; +use PhpMyAdmin\RelationCleanup; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Sql; +use PhpMyAdmin\Template; +use PhpMyAdmin\Transformations; +use PhpMyAdmin\Util; +use PhpMyAdmin\Utils\ForeignKey; + +use function __; +use function count; + +final class EmptyTableController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var Relation */ + private $relation; + + /** @var RelationCleanup */ + private $relationCleanup; + + /** @var Operations */ + private $operations; + + /** @var FlashMessages */ + private $flash; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct( + $response, + Template $template, + $db, + $dbi, + Relation $relation, + RelationCleanup $relationCleanup, + Operations $operations, + FlashMessages $flash, + StructureController $structureController + ) { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->relation = $relation; + $this->relationCleanup = $relationCleanup; + $this->operations = $operations; + $this->flash = $flash; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $table, $message, $sql_query; + + $multBtn = $_POST['mult_btn'] ?? ''; + $selected = $_POST['selected'] ?? []; + + if ($multBtn !== __('Yes')) { + $this->flash->addMessage('success', __('No change')); + $this->redirect('/database/structure', ['db' => $db]); + + return; + } + + $defaultFkCheckValue = ForeignKey::handleDisableCheckInit(); + + $sql_query = ''; + $selectedCount = count($selected); + + for ($i = 0; $i < $selectedCount; $i++) { + $aQuery = 'TRUNCATE '; + $aQuery .= Util::backquote($selected[$i]); + + $sql_query .= $aQuery . ';' . "\n"; + $this->dbi->selectDb($db); + $this->dbi->query($aQuery); + } + + if (! empty($_REQUEST['pos'])) { + $sql = new Sql( + $this->dbi, + $this->relation, + $this->relationCleanup, + $this->operations, + new Transformations(), + $this->template + ); + + $_REQUEST['pos'] = $sql->calculatePosForLastPage($db, $table, $_REQUEST['pos']); + } + + ForeignKey::handleDisableCheckCleanup($defaultFkCheckValue); + + $message = Message::success(); + + if (empty($_POST['message'])) { + $_POST['message'] = $message; + } + + unset($_POST['mult_btn']); + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/FavoriteTableController.php b/libraries/classes/Controllers/Database/Structure/FavoriteTableController.php new file mode 100644 index 0000000000..596dc6de9d --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/FavoriteTableController.php @@ -0,0 +1,193 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\RecentFavoriteTable; +use PhpMyAdmin\Relation; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Url; +use PhpMyAdmin\Util; + +use function __; +use function count; +use function json_decode; +use function json_encode; +use function md5; +use function sha1; + +final class FavoriteTableController extends AbstractController +{ + /** @var Relation */ + private $relation; + + /** + * @param ResponseRenderer $response + * @param string $db + */ + public function __construct($response, Template $template, $db, Relation $relation) + { + parent::__construct($response, $template, $db); + $this->relation = $relation; + } + + public function __invoke(): void + { + global $cfg, $db, $errorUrl; + + $parameters = [ + 'favorite_table' => $_REQUEST['favorite_table'] ?? null, + 'favoriteTables' => $_REQUEST['favoriteTables'] ?? null, + 'sync_favorite_tables' => $_REQUEST['sync_favorite_tables'] ?? null, + ]; + + Util::checkParameters(['db']); + + $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); + $errorUrl .= Url::getCommon(['db' => $db], '&'); + + if (! $this->hasDatabase() || ! $this->response->isAjax()) { + return; + } + + $favoriteInstance = RecentFavoriteTable::getInstance('favorite'); + if (isset($parameters['favoriteTables'])) { + $favoriteTables = json_decode($parameters['favoriteTables'], true); + } else { + $favoriteTables = []; + } + + // Required to keep each user's preferences separate. + $user = sha1($cfg['Server']['user']); + + // Request for Synchronization of favorite tables. + if (isset($parameters['sync_favorite_tables'])) { + $cfgRelation = $this->relation->getRelationsParam(); + if ($cfgRelation['favoritework']) { + $this->response->addJSON($this->synchronizeFavoriteTables( + $favoriteInstance, + $user, + $favoriteTables + )); + } + + return; + } + + $changes = true; + $favoriteTable = $parameters['favorite_table'] ?? ''; + $alreadyFavorite = $this->checkFavoriteTable($favoriteTable); + + if (isset($_REQUEST['remove_favorite'])) { + if ($alreadyFavorite) { + // If already in favorite list, remove it. + $favoriteInstance->remove($this->db, $favoriteTable); + $alreadyFavorite = false; // for favorite_anchor template + } + } elseif (isset($_REQUEST['add_favorite'])) { + if (! $alreadyFavorite) { + $numTables = count($favoriteInstance->getTables()); + if ($numTables == $cfg['NumFavoriteTables']) { + $changes = false; + } else { + // Otherwise add to favorite list. + $favoriteInstance->add($this->db, $favoriteTable); + $alreadyFavorite = true; // for favorite_anchor template + } + } + } + + $favoriteTables[$user] = $favoriteInstance->getTables(); + + $json = []; + $json['changes'] = $changes; + if (! $changes) { + $json['message'] = $this->template->render('components/error_message', [ + 'msg' => __('Favorite List is full!'), + ]); + $this->response->addJSON($json); + + return; + } + + // Check if current table is already in favorite list. + $favoriteParams = [ + 'db' => $this->db, + 'ajax_request' => true, + 'favorite_table' => $favoriteTable, + ($alreadyFavorite ? 'remove' : 'add') . '_favorite' => true, + ]; + + $json['user'] = $user; + $json['favoriteTables'] = json_encode($favoriteTables); + $json['list'] = $favoriteInstance->getHtmlList(); + $json['anchor'] = $this->template->render('database/structure/favorite_anchor', [ + 'table_name_hash' => md5($favoriteTable), + 'db_table_name_hash' => md5($this->db . '.' . $favoriteTable), + 'fav_params' => $favoriteParams, + 'already_favorite' => $alreadyFavorite, + ]); + + $this->response->addJSON($json); + } + + /** + * Synchronize favorite tables + * + * @param RecentFavoriteTable $favoriteInstance Instance of this class + * @param string $user The user hash + * @param array $favoriteTables Existing favorites + * + * @return array + */ + private function synchronizeFavoriteTables( + RecentFavoriteTable $favoriteInstance, + string $user, + array $favoriteTables + ): array { + $favoriteInstanceTables = $favoriteInstance->getTables(); + + if ( + empty($favoriteInstanceTables) + && isset($favoriteTables[$user]) + ) { + foreach ($favoriteTables[$user] as $key => $value) { + $favoriteInstance->add($value['db'], $value['table']); + } + } + + $favoriteTables[$user] = $favoriteInstance->getTables(); + + $json = [ + 'favoriteTables' => json_encode($favoriteTables), + 'list' => $favoriteInstance->getHtmlList(), + ]; + $serverId = $GLOBALS['server']; + // Set flag when localStorage and pmadb(if present) are in sync. + $_SESSION['tmpval']['favorites_synced'][$serverId] = true; + + return $json; + } + + /** + * Function to check if a table is already in favorite list. + * + * @param string $currentTable current table + */ + private function checkFavoriteTable(string $currentTable): bool + { + // ensure $_SESSION['tmpval']['favoriteTables'] is initialized + RecentFavoriteTable::getInstance('favorite'); + $favoriteTables = $_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']] ?? []; + foreach ($favoriteTables as $value) { + if ($value['db'] == $this->db && $value['table'] == $currentTable) { + return true; + } + } + + return false; + } +} diff --git a/libraries/classes/Controllers/Database/Structure/RealRowCountController.php b/libraries/classes/Controllers/Database/Structure/RealRowCountController.php new file mode 100644 index 0000000000..1446140793 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/RealRowCountController.php @@ -0,0 +1,84 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Url; +use PhpMyAdmin\Util; + +use function json_encode; + +/** + * Handles request for real row count on database level view page. + */ +final class RealRowCountController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + } + + public function __invoke(): void + { + global $cfg, $db, $errorUrl; + + $parameters = [ + 'real_row_count_all' => $_REQUEST['real_row_count_all'] ?? null, + 'table' => $_REQUEST['table'] ?? null, + ]; + + Util::checkParameters(['db']); + + $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); + $errorUrl .= Url::getCommon(['db' => $db], '&'); + + if (! $this->hasDatabase() || ! $this->response->isAjax()) { + return; + } + + [$tables] = Util::getDbInfo($this->db, '_structure'); + + // If there is a request to update all table's row count. + if (! isset($parameters['real_row_count_all'])) { + // Get the real row count for the table. + $realRowCount = (int) $this->dbi + ->getTable($this->db, (string) $parameters['table']) + ->getRealRowCountTable(); + // Format the number. + $realRowCount = Util::formatNumber($realRowCount, 0); + + $this->response->addJSON(['real_row_count' => $realRowCount]); + + return; + } + + // Array to store the results. + $realRowCountAll = []; + // Iterate over each table and fetch real row count. + foreach ($tables as $table) { + $rowCount = $this->dbi + ->getTable($this->db, $table['TABLE_NAME']) + ->getRealRowCountTable(); + $realRowCountAll[] = [ + 'table' => $table['TABLE_NAME'], + 'row_count' => $rowCount, + ]; + } + + $this->response->addJSON(['real_row_count_all' => json_encode($realRowCountAll)]); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/ReplacePrefixController.php b/libraries/classes/Controllers/Database/Structure/ReplacePrefixController.php new file mode 100644 index 0000000000..2ac587aaa9 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/ReplacePrefixController.php @@ -0,0 +1,76 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Controllers\Database\StructureController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Util; + +use function count; +use function mb_strlen; +use function mb_substr; + +final class ReplacePrefixController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var StructureController */ + private $structureController; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi, StructureController $structureController) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + $this->structureController = $structureController; + } + + public function __invoke(): void + { + global $db, $message, $sql_query; + + $selected = $_POST['selected'] ?? []; + $fromPrefix = $_POST['from_prefix'] ?? ''; + $toPrefix = $_POST['to_prefix'] ?? ''; + + $sql_query = ''; + $selectedCount = count($selected); + + for ($i = 0; $i < $selectedCount; $i++) { + $current = $selected[$i]; + $subFromPrefix = mb_substr($current, 0, mb_strlen((string) $fromPrefix)); + + if ($subFromPrefix === $fromPrefix) { + $newTableName = $toPrefix . mb_substr($current, mb_strlen((string) $fromPrefix)); + } else { + $newTableName = $current; + } + + $aQuery = 'ALTER TABLE ' . Util::backquote($selected[$i]) + . ' RENAME ' . Util::backquote($newTableName); + + $sql_query .= $aQuery . ';' . "\n"; + $this->dbi->selectDb($db); + $this->dbi->query($aQuery); + } + + $message = Message::success(); + + if (empty($_POST['message'])) { + $_POST['message'] = $message; + } + + ($this->structureController)(); + } +} diff --git a/libraries/classes/Controllers/Database/Structure/ShowCreateController.php b/libraries/classes/Controllers/Database/Structure/ShowCreateController.php new file mode 100644 index 0000000000..f148316e94 --- /dev/null +++ b/libraries/classes/Controllers/Database/Structure/ShowCreateController.php @@ -0,0 +1,69 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\AbstractController; +use PhpMyAdmin\Core; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; + +use function __; + +final class ShowCreateController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** + * @param ResponseRenderer $response + * @param string $db + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $db, $dbi) + { + parent::__construct($response, $template, $db); + $this->dbi = $dbi; + } + + public function __invoke(): void + { + $selected = $_POST['selected_tbl'] ?? []; + + if (empty($selected)) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', __('No table selected.')); + + return; + } + + $tables = $this->getShowCreateTables($selected); + + $showCreate = $this->template->render('database/structure/show_create', ['tables' => $tables]); + + $this->response->addJSON('message', $showCreate); + } + + /** + * @param string[] $selected Selected tables. + * + * @return array<string, array<int, array<string, string>>> + */ + private function getShowCreateTables(array $selected): array + { + $tables = ['tables' => [], 'views' => []]; + + foreach ($selected as $table) { + $object = $this->dbi->getTable($this->db, $table); + + $tables[$object->isView() ? 'views' : 'tables'][] = [ + 'name' => Core::mimeDefaultFunction($table), + 'show_create' => Core::mimeDefaultFunction($object->showCreate()), + ]; + } + + return $tables; + } +} diff --git a/libraries/classes/Controllers/Database/StructureController.php b/libraries/classes/Controllers/Database/StructureController.php index 00b65caeaf..3bbd1e5b70 100644 --- a/libraries/classes/Controllers/Database/StructureController.php +++ b/libraries/classes/Controllers/Database/StructureController.php @@ -7,12 +7,9 @@ namespace PhpMyAdmin\Controllers\Database; use PhpMyAdmin\Charsets; use PhpMyAdmin\CheckUserPrivileges; use PhpMyAdmin\Config\PageSettings; -use PhpMyAdmin\Core; -use PhpMyAdmin\Database\CentralColumns; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\FlashMessages; use PhpMyAdmin\Html\Generator; -use PhpMyAdmin\Message; use PhpMyAdmin\Operations; use PhpMyAdmin\RecentFavoriteTable; use PhpMyAdmin\Relation; @@ -21,15 +18,11 @@ use PhpMyAdmin\Replication; use PhpMyAdmin\ReplicationInfo; use PhpMyAdmin\ResponseRenderer; use PhpMyAdmin\Sanitize; -use PhpMyAdmin\Sql; use PhpMyAdmin\StorageEngine; -use PhpMyAdmin\Table; use PhpMyAdmin\Template; use PhpMyAdmin\Tracker; -use PhpMyAdmin\Transformations; use PhpMyAdmin\Url; use PhpMyAdmin\Util; -use PhpMyAdmin\Utils\ForeignKey; use function __; use function array_search; @@ -39,15 +32,11 @@ use function htmlspecialchars; use function implode; use function in_array; use function is_string; -use function json_decode; -use function json_encode; use function max; -use function mb_strlen; use function mb_substr; use function md5; use function preg_match; use function preg_quote; -use function sha1; use function sprintf; use function str_replace; use function strlen; @@ -151,7 +140,7 @@ class StructureController extends AbstractController $this->isShowStats = $isShowStats; } - public function index(): void + public function __invoke(): void { global $cfg, $db, $errorUrl; @@ -235,198 +224,6 @@ class StructureController extends AbstractController ]); } - public function addRemoveFavoriteTablesAction(): void - { - global $cfg, $db, $errorUrl; - - $parameters = [ - 'favorite_table' => $_REQUEST['favorite_table'] ?? null, - 'favoriteTables' => $_REQUEST['favoriteTables'] ?? null, - 'sync_favorite_tables' => $_REQUEST['sync_favorite_tables'] ?? null, - ]; - - Util::checkParameters(['db']); - - $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); - $errorUrl .= Url::getCommon(['db' => $db], '&'); - - if (! $this->hasDatabase() || ! $this->response->isAjax()) { - return; - } - - $favoriteInstance = RecentFavoriteTable::getInstance('favorite'); - if (isset($parameters['favoriteTables'])) { - $favoriteTables = json_decode($parameters['favoriteTables'], true); - } else { - $favoriteTables = []; - } - - // Required to keep each user's preferences separate. - $user = sha1($cfg['Server']['user']); - - // Request for Synchronization of favorite tables. - if (isset($parameters['sync_favorite_tables'])) { - $cfgRelation = $this->relation->getRelationsParam(); - if ($cfgRelation['favoritework']) { - $this->response->addJSON($this->synchronizeFavoriteTables( - $favoriteInstance, - $user, - $favoriteTables - )); - } - - return; - } - - $changes = true; - $favoriteTable = $parameters['favorite_table'] ?? ''; - $alreadyFavorite = $this->checkFavoriteTable($favoriteTable); - - if (isset($_REQUEST['remove_favorite'])) { - if ($alreadyFavorite) { - // If already in favorite list, remove it. - $favoriteInstance->remove($this->db, $favoriteTable); - $alreadyFavorite = false; // for favorite_anchor template - } - } elseif (isset($_REQUEST['add_favorite'])) { - if (! $alreadyFavorite) { - $numTables = count($favoriteInstance->getTables()); - if ($numTables == $cfg['NumFavoriteTables']) { - $changes = false; - } else { - // Otherwise add to favorite list. - $favoriteInstance->add($this->db, $favoriteTable); - $alreadyFavorite = true; // for favorite_anchor template - } - } - } - - $favoriteTables[$user] = $favoriteInstance->getTables(); - - $json = []; - $json['changes'] = $changes; - if (! $changes) { - $json['message'] = $this->template->render('components/error_message', [ - 'msg' => __('Favorite List is full!'), - ]); - $this->response->addJSON($json); - - return; - } - - // Check if current table is already in favorite list. - $favoriteParams = [ - 'db' => $this->db, - 'ajax_request' => true, - 'favorite_table' => $favoriteTable, - ($alreadyFavorite ? 'remove' : 'add') . '_favorite' => true, - ]; - - $json['user'] = $user; - $json['favoriteTables'] = json_encode($favoriteTables); - $json['list'] = $favoriteInstance->getHtmlList(); - $json['anchor'] = $this->template->render('database/structure/favorite_anchor', [ - 'table_name_hash' => md5($favoriteTable), - 'db_table_name_hash' => md5($this->db . '.' . $favoriteTable), - 'fav_params' => $favoriteParams, - 'already_favorite' => $alreadyFavorite, - ]); - - $this->response->addJSON($json); - } - - /** - * Handles request for real row count on database level view page. - */ - public function handleRealRowCountRequestAction(): void - { - global $cfg, $db, $errorUrl; - - $parameters = [ - 'real_row_count_all' => $_REQUEST['real_row_count_all'] ?? null, - 'table' => $_REQUEST['table'] ?? null, - ]; - - Util::checkParameters(['db']); - - $errorUrl = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database'); - $errorUrl .= Url::getCommon(['db' => $db], '&'); - - if (! $this->hasDatabase() || ! $this->response->isAjax()) { - return; - } - - // If there is a request to update all table's row count. - if (! isset($parameters['real_row_count_all'])) { - // Get the real row count for the table. - $realRowCount = (int) $this->dbi - ->getTable($this->db, (string) $parameters['table']) - ->getRealRowCountTable(); - // Format the number. - $realRowCount = Util::formatNumber($realRowCount, 0); - - $this->response->addJSON(['real_row_count' => $realRowCount]); - - return; - } - - // Array to store the results. - $realRowCountAll = []; - // Iterate over each table and fetch real row count. - foreach ($this->tables as $table) { - $rowCount = $this->dbi - ->getTable($this->db, $table['TABLE_NAME']) - ->getRealRowCountTable(); - $realRowCountAll[] = [ - 'table' => $table['TABLE_NAME'], - 'row_count' => $rowCount, - ]; - } - - $this->response->addJSON(['real_row_count_all' => json_encode($realRowCountAll)]); - } - - public function copyTable(): void - { - global $db, $message; - - $selected = $_POST['selected'] ?? []; - $targetDb = $_POST['target_db'] ?? null; - $selectedCount = count($selected); - - for ($i = 0; $i < $selectedCount; $i++) { - Table::moveCopy( - $db, - $selected[$i], - $targetDb, - $selected[$i], - $_POST['what'], - false, - 'one_table', - isset($_POST['drop_if_exists']) && $_POST['drop_if_exists'] === 'true' - ); - - if (empty($_POST['adjust_privileges'])) { - continue; - } - - $this->operations->adjustPrivilegesCopyTable( - $db, - $selected[$i], - $targetDb, - $selected[$i] - ); - } - - $message = Message::success(); - - if (empty($_POST['message'])) { - $_POST['message'] = $message; - } - - $this->index(); - } - /** * @param array $replicaInfo */ @@ -893,44 +690,6 @@ class StructureController extends AbstractController } /** - * Synchronize favorite tables - * - * @param RecentFavoriteTable $favoriteInstance Instance of this class - * @param string $user The user hash - * @param array $favoriteTables Existing favorites - * - * @return array - */ - protected function synchronizeFavoriteTables( - RecentFavoriteTable $favoriteInstance, - string $user, - array $favoriteTables - ): array { - $favoriteInstanceTables = $favoriteInstance->getTables(); - - if ( - empty($favoriteInstanceTables) - && isset($favoriteTables[$user]) - ) { - foreach ($favoriteTables[$user] as $key => $value) { - $favoriteInstance->add($value['db'], $value['table']); - } - } - - $favoriteTables[$user] = $favoriteInstance->getTables(); - - $json = [ - 'favoriteTables' => json_encode($favoriteTables), - 'list' => $favoriteInstance->getHtmlList(), - ]; - $serverId = $GLOBALS['server']; - // Set flag when localStorage and pmadb(if present) are in sync. - $_SESSION['tmpval']['favorites_synced'][$serverId] = true; - - return $json; - } - - /** * Function to check if a table is already in favorite list. * * @param string $currentTable current table @@ -1240,524 +999,4 @@ class StructureController extends AbstractController $sumSize, ]; } - - public function showCreate(): void - { - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $tables = $this->getShowCreateTables($selected); - - $showCreate = $this->template->render('database/structure/show_create', ['tables' => $tables]); - - $this->response->addJSON('message', $showCreate); - } - - /** - * @param string[] $selected Selected tables. - * - * @return array<string, array<int, array<string, string>>> - */ - private function getShowCreateTables(array $selected): array - { - $tables = ['tables' => [], 'views' => []]; - - foreach ($selected as $table) { - $object = $this->dbi->getTable($this->db, $table); - - $tables[$object->isView() ? 'views' : 'tables'][] = [ - 'name' => Core::mimeDefaultFunction($table), - 'show_create' => Core::mimeDefaultFunction($object->showCreate()), - ]; - } - - return $tables; - } - - public function copyForm(): void - { - global $db, $dblist; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $urlParams = ['db' => $db]; - foreach ($selected as $selectedValue) { - $urlParams['selected'][] = $selectedValue; - } - - $databasesList = $dblist->databases; - foreach ($databasesList as $key => $databaseName) { - if ($databaseName == $db) { - $databasesList->offsetUnset($key); - break; - } - } - - $this->response->disable(); - $this->render('database/structure/copy_form', [ - 'url_params' => $urlParams, - 'options' => $databasesList->getList(), - ]); - } - - public function centralColumnsAdd(): void - { - global $message; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $centralColumns = new CentralColumns($this->dbi); - $error = $centralColumns->syncUniqueColumns($selected); - - $message = $error instanceof Message ? $error : Message::success(__('Success!')); - - unset($_POST['submit_mult']); - - $this->index(); - } - - public function centralColumnsMakeConsistent(): void - { - global $db, $message; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $centralColumns = new CentralColumns($this->dbi); - $error = $centralColumns->makeConsistentWithList($db, $selected); - - $message = $error instanceof Message ? $error : Message::success(__('Success!')); - - unset($_POST['submit_mult']); - - $this->index(); - } - - public function centralColumnsRemove(): void - { - global $message; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $centralColumns = new CentralColumns($this->dbi); - $error = $centralColumns->deleteColumnsFromList($_POST['db'], $selected); - - $message = $error instanceof Message ? $error : Message::success(__('Success!')); - - unset($_POST['submit_mult']); - - $this->index(); - } - - public function addPrefix(): void - { - global $db; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $params = ['db' => $db]; - foreach ($selected as $selectedValue) { - $params['selected'][] = $selectedValue; - } - - $this->response->disable(); - $this->render('database/structure/add_prefix', ['url_params' => $params]); - } - - public function changePrefixForm(): void - { - global $db; - - $selected = $_POST['selected_tbl'] ?? []; - $submitMult = $_POST['submit_mult'] ?? ''; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $route = '/database/structure/replace-prefix'; - if ($submitMult === 'copy_tbl_change_prefix') { - $route = '/database/structure/copy-table-with-prefix'; - } - - $urlParams = ['db' => $db]; - foreach ($selected as $selectedValue) { - $urlParams['selected'][] = $selectedValue; - } - - $this->response->disable(); - $this->render('database/structure/change_prefix_form', [ - 'route' => $route, - 'url_params' => $urlParams, - ]); - } - - public function dropForm(): void - { - global $db; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $views = $this->dbi->getVirtualTables($db); - - $fullQueryViews = ''; - $fullQuery = ''; - - foreach ($selected as $selectedValue) { - $current = $selectedValue; - if (! empty($views) && in_array($current, $views)) { - $fullQueryViews .= (empty($fullQueryViews) ? 'DROP VIEW ' : ', ') - . Util::backquote(htmlspecialchars($current)); - } else { - $fullQuery .= (empty($fullQuery) ? 'DROP TABLE ' : ', ') - . Util::backquote(htmlspecialchars($current)); - } - } - - if (! empty($fullQuery)) { - $fullQuery .= ';<br>' . "\n"; - } - - if (! empty($fullQueryViews)) { - $fullQuery .= $fullQueryViews . ';<br>' . "\n"; - } - - $urlParams = ['db' => $db]; - foreach ($selected as $selectedValue) { - $urlParams['selected'][] = $selectedValue; - } - - foreach ($views as $current) { - $urlParams['views'][] = $current; - } - - $this->render('database/structure/drop_form', [ - 'url_params' => $urlParams, - 'full_query' => $fullQuery, - 'is_foreign_key_check' => ForeignKey::isCheckEnabled(), - ]); - } - - public function emptyForm(): void - { - global $db; - - $selected = $_POST['selected_tbl'] ?? []; - - if (empty($selected)) { - $this->response->setRequestStatus(false); - $this->response->addJSON('message', __('No table selected.')); - - return; - } - - $fullQuery = ''; - $urlParams = ['db' => $db]; - - foreach ($selected as $selectedValue) { - $fullQuery .= 'TRUNCATE '; - $fullQuery .= Util::backquote(htmlspecialchars($selectedValue)) . ';<br>'; - $urlParams['selected'][] = $selectedValue; - } - - $this->render('database/structure/empty_form', [ - 'url_params' => $urlParams, - 'full_query' => $fullQuery, - 'is_foreign_key_check' => ForeignKey::isCheckEnabled(), - ]); - } - - public function dropTable(): void - { - global $db, $message, $reload, $sql_query; - - $reload = $_POST['reload'] ?? $reload ?? null; - $multBtn = $_POST['mult_btn'] ?? ''; - $selected = $_POST['selected'] ?? []; - - $views = $this->dbi->getVirtualTables($db); - - if ($multBtn !== __('Yes')) { - $message = Message::success(__('No change')); - - if (empty($_POST['message'])) { - $_POST['message'] = Message::success(); - } - - unset($_POST['mult_btn']); - - $this->index(); - - return; - } - - $defaultFkCheckValue = ForeignKey::handleDisableCheckInit(); - $sql_query = ''; - $sqlQueryViews = ''; - $selectedCount = count($selected); - - for ($i = 0; $i < $selectedCount; $i++) { - $this->relationCleanup->table($db, $selected[$i]); - $current = $selected[$i]; - - if (! empty($views) && in_array($current, $views)) { - $sqlQueryViews .= (empty($sqlQueryViews) ? 'DROP VIEW ' : ', ') . Util::backquote($current); - } else { - $sql_query .= (empty($sql_query) ? 'DROP TABLE ' : ', ') . Util::backquote($current); - } - - $reload = 1; - } - - if (! empty($sql_query)) { - $sql_query .= ';'; - } elseif (! empty($sqlQueryViews)) { - $sql_query = $sqlQueryViews . ';'; - unset($sqlQueryViews); - } - - // Unset cache values for tables count, issue #14205 - if (isset($_SESSION['tmpval'])) { - if (isset($_SESSION['tmpval']['table_limit_offset'])) { - unset($_SESSION['tmpval']['table_limit_offset']); - } - - if (isset($_SESSION['tmpval']['table_limit_offset_db'])) { - unset($_SESSION['tmpval']['table_limit_offset_db']); - } - } - - $this->dbi->selectDb($db); - $result = $this->dbi->tryQuery($sql_query); - - if ($result && ! empty($sqlQueryViews)) { - $sql_query .= ' ' . $sqlQueryViews . ';'; - $result = $this->dbi->tryQuery($sqlQueryViews); - unset($sqlQueryViews); - } - - if (! $result) { - $message = Message::error((string) $this->dbi->getError()); - } - - ForeignKey::handleDisableCheckCleanup($defaultFkCheckValue); - - $message = Message::success(); - - if (empty($_POST['message'])) { - $_POST['message'] = $message; - } - - unset($_POST['mult_btn']); - - $this->index(); - } - - public function emptyTable(): void - { - global $db, $table, $message, $sql_query; - - $multBtn = $_POST['mult_btn'] ?? ''; - $selected = $_POST['selected'] ?? []; - - if ($multBtn !== __('Yes')) { - $this->flash->addMessage('success', __('No change')); - $this->redirect('/database/structure', ['db' => $db]); - - return; - } - - $defaultFkCheckValue = ForeignKey::handleDisableCheckInit(); - - $sql_query = ''; - $selectedCount = count($selected); - - for ($i = 0; $i < $selectedCount; $i++) { - $aQuery = 'TRUNCATE '; - $aQuery .= Util::backquote($selected[$i]); - - $sql_query .= $aQuery . ';' . "\n"; - $this->dbi->selectDb($db); - $this->dbi->query($aQuery); - } - - if (! empty($_REQUEST['pos'])) { - $sql = new Sql( - $this->dbi, - $this->relation, - $this->relationCleanup, - $this->operations, - new Transformations(), - $this->template - ); - - $_REQUEST['pos'] = $sql->calculatePosForLastPage($db, $table, $_REQUEST['pos']); - } - - ForeignKey::handleDisableCheckCleanup($defaultFkCheckValue); - - $message = Message::success(); - - if (empty($_POST['message'])) { - $_POST['message'] = $message; - } - - unset($_POST['mult_btn']); - - $this->index(); - } - - public function addPrefixTable(): void - { - global $db, $message, $sql_query; - - $selected = $_POST['selected'] ?? []; - - $sql_query = ''; - $selectedCount = count($selected); - - for ($i = 0; $i < $selectedCount; $i++) { - $newTableName = $_POST['add_prefix'] . $selected[$i]; - $aQuery = 'ALTER TABLE ' . Util::backquote($selected[$i]) - . ' RENAME ' . Util::backquote($newTableName); - - $sql_query .= $aQuery . ';' . "\n"; - $this->dbi->selectDb($db); - $this->dbi->query($aQuery); - } - - $message = Message::success(); - - if (empty($_POST['message'])) { - $_POST['message'] = $message; - } - - $this->index(); - } - - public function replacePrefix(): void - { - global $db, $message, $sql_query; - - $selected = $_POST['selected'] ?? []; - $fromPrefix = $_POST['from_prefix'] ?? ''; - $toPrefix = $_POST['to_prefix'] ?? ''; - - $sql_query = ''; - $selectedCount = count($selected); - - for ($i = 0; $i < $selectedCount; $i++) { - $current = $selected[$i]; - $subFromPrefix = mb_substr($current, 0, mb_strlen((string) $fromPrefix)); - - if ($subFromPrefix === $fromPrefix) { - $newTableName = $toPrefix . mb_substr( - $current, - mb_strlen((string) $fromPrefix) - ); - } else { - $newTableName = $current; - } - - $aQuery = 'ALTER TABLE ' . Util::backquote($selected[$i]) - . ' RENAME ' . Util::backquote($newTableName); - - $sql_query .= $aQuery . ';' . "\n"; - $this->dbi->selectDb($db); - $this->dbi->query($aQuery); - } - - $message = Message::success(); - - if (empty($_POST['message'])) { - $_POST['message'] = $message; - } - - $this->index(); - } - - public function copyTableWithPrefix(): void - { - global $db, $message; - - $selected = $_POST['selected'] ?? []; - $fromPrefix = $_POST['from_prefix'] ?? null; - $toPrefix = $_POST['to_prefix'] ?? null; - - $selectedCount = count($selected); - - for ($i = 0; $i < $selectedCount; $i++) { - $current = $selected[$i]; - $newTableName = $toPrefix . mb_substr($current, mb_strlen((string) $fromPrefix)); - - Table::moveCopy( - $db, - $current, - $db, - $newTableName, - 'data', - false, - 'one_table', - isset($_POST['drop_if_exists']) && $_POST['drop_if_exists'] === 'true' - ); - } - - $message = Message::success(); - - if (empty($_POST['message'])) { - $_POST['message'] = $message; - } - - $this->index(); - } } diff --git a/libraries/classes/Controllers/Export/CheckTimeOutController.php b/libraries/classes/Controllers/Export/CheckTimeOutController.php new file mode 100644 index 0000000000..10da315083 --- /dev/null +++ b/libraries/classes/Controllers/Export/CheckTimeOutController.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Export; + +use PhpMyAdmin\Controllers\AbstractController; + +final class CheckTimeOutController extends AbstractController +{ + public function __invoke(): void + { + $this->response->setAjax(true); + + if (isset($_SESSION['pma_export_error'])) { + unset($_SESSION['pma_export_error']); + $this->response->addJSON('message', 'timeout'); + + return; + } + + $this->response->addJSON('message', 'success'); + } +} diff --git a/libraries/classes/Controllers/Export/ExportController.php b/libraries/classes/Controllers/Export/ExportController.php index 2c514890e8..78f28a7514 100644 --- a/libraries/classes/Controllers/Export/ExportController.php +++ b/libraries/classes/Controllers/Export/ExportController.php @@ -718,18 +718,4 @@ final class ExportController extends AbstractController echo $this->export->dumpBuffer; } - - public function checkTimeOut(): void - { - $this->response->setAjax(true); - - if (isset($_SESSION['pma_export_error'])) { - unset($_SESSION['pma_export_error']); - $this->response->addJSON('message', 'timeout'); - - return; - } - - $this->response->addJSON('message', 'success'); - } } diff --git a/libraries/classes/Controllers/Server/Databases/CreateController.php b/libraries/classes/Controllers/Server/Databases/CreateController.php new file mode 100644 index 0000000000..0c6b1442b0 --- /dev/null +++ b/libraries/classes/Controllers/Server/Databases/CreateController.php @@ -0,0 +1,119 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Server\Databases; + +use PhpMyAdmin\Charsets; +use PhpMyAdmin\Controllers\AbstractController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Html\Generator; +use PhpMyAdmin\Message; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Url; +use PhpMyAdmin\Util; + +use function __; +use function array_key_exists; +use function explode; +use function mb_strlen; +use function mb_strtolower; +use function str_contains; + +final class CreateController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** + * @param ResponseRenderer $response + * @param DatabaseInterface $dbi + */ + public function __construct($response, Template $template, $dbi) + { + parent::__construct($response, $template); + $this->dbi = $dbi; + } + + public function __invoke(): void + { + global $cfg, $db; + + $params = [ + 'new_db' => $_POST['new_db'] ?? null, + 'db_collation' => $_POST['db_collation'] ?? null, + ]; + + if (! isset($params['new_db']) || mb_strlen($params['new_db']) === 0 || ! $this->response->isAjax()) { + $this->response->addJSON(['message' => Message::error()]); + + return; + } + + // lower_case_table_names=1 `DB` becomes `db` + if ($this->dbi->getLowerCaseNames() === '1') { + $params['new_db'] = mb_strtolower( + $params['new_db'] + ); + } + + /** + * Builds and executes the db creation sql query + */ + $sqlQuery = 'CREATE DATABASE ' . Util::backquote($params['new_db']); + if (! empty($params['db_collation'])) { + [$databaseCharset] = explode('_', $params['db_collation']); + $charsets = Charsets::getCharsets( + $this->dbi, + $cfg['Server']['DisableIS'] + ); + $collations = Charsets::getCollations( + $this->dbi, + $cfg['Server']['DisableIS'] + ); + if ( + array_key_exists($databaseCharset, $charsets) + && array_key_exists($params['db_collation'], $collations[$databaseCharset]) + ) { + $sqlQuery .= ' DEFAULT' + . Util::getCharsetQueryPart($params['db_collation']); + } + } + + $sqlQuery .= ';'; + + $result = $this->dbi->tryQuery($sqlQuery); + + if (! $result) { + // avoid displaying the not-created db name in header or navi panel + $db = ''; + + $message = Message::rawError((string) $this->dbi->getError()); + $json = ['message' => $message]; + + $this->response->setRequestStatus(false); + } else { + $db = $params['new_db']; + + $message = Message::success(__('Database %1$s has been created.')); + $message->addParam($params['new_db']); + + $scriptName = Util::getScriptNameForOption( + $cfg['DefaultTabDatabase'], + 'database' + ); + + $json = [ + 'message' => $message, + 'sql_query' => Generator::getMessage('', $sqlQuery, 'success'), + 'url' => $scriptName . Url::getCommon( + ['db' => $params['new_db']], + ! str_contains($scriptName, '?') ? '?' : '&' + ), + ]; + } + + $this->response->addJSON($json); + } +} diff --git a/libraries/classes/Controllers/Server/Databases/DestroyController.php b/libraries/classes/Controllers/Server/Databases/DestroyController.php new file mode 100644 index 0000000000..4f7d50cf0f --- /dev/null +++ b/libraries/classes/Controllers/Server/Databases/DestroyController.php @@ -0,0 +1,122 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Controllers\Server\Databases; + +use PhpMyAdmin\Controllers\AbstractController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Message; +use PhpMyAdmin\RelationCleanup; +use PhpMyAdmin\ResponseRenderer; +use PhpMyAdmin\Template; +use PhpMyAdmin\Transformations; +use PhpMyAdmin\Url; +use PhpMyAdmin\Util; + +use function __; +use function _ngettext; +use function count; + +final class DestroyController extends AbstractController +{ + /** @var DatabaseInterface */ + private $dbi; + + /** @var Transformations */ + private $transformations; + + /** @var RelationCleanup */ + private $relationCleanup; + + /** + * @param ResponseRenderer $response + * @param DatabaseInterface $dbi + */ + public function __construct( + $response, + Template $template, + $dbi, + Transformations $transformations, + RelationCleanup $relationCleanup + ) { + parent::__construct($response, $template); + $this->dbi = $dbi; + $this->transformations = $transformations; + $this->relationCleanup = $relationCleanup; + } + + public function __invoke(): void + { + global $selected, $errorUrl, $cfg, $dblist, $reload; + + $params = [ + 'drop_selected_dbs' => $_POST['drop_selected_dbs'] ?? null, + 'selected_dbs' => $_POST['selected_dbs'] ?? null, + ]; + /** @var Message|int $message */ + $message = -1; + + if ( + ! isset($params['drop_selected_dbs']) + || ! $this->response->isAjax() + || (! $this->dbi->isSuperUser() && ! $cfg['AllowUserDropDatabase']) + ) { + $message = Message::error(); + $json = ['message' => $message]; + $this->response->setRequestStatus($message->isSuccess()); + $this->response->addJSON($json); + + return; + } + + if (! isset($params['selected_dbs'])) { + $message = Message::error(__('No databases selected.')); + $json = ['message' => $message]; + $this->response->setRequestStatus($message->isSuccess()); + $this->response->addJSON($json); + + return; + } + + $errorUrl = Url::getFromRoute('/server/databases'); + $selected = $_POST['selected_dbs']; + $rebuildDatabaseList = false; + $sqlQuery = ''; + $numberOfDatabases = count($selected); + + for ($i = 0; $i < $numberOfDatabases; $i++) { + $this->relationCleanup->database($selected[$i]); + $aQuery = 'DROP DATABASE ' . Util::backquote($selected[$i]); + $reload = true; + $rebuildDatabaseList = true; + + $sqlQuery .= $aQuery . ';' . "\n"; + $this->dbi->query($aQuery); + $this->transformations->clear($selected[$i]); + } + + if ($rebuildDatabaseList) { + $dblist->databases->build(); + } + + if ($message === -1) { // no error message + $message = Message::success( + _ngettext( + '%1$d database has been dropped successfully.', + '%1$d databases have been dropped successfully.', + $numberOfDatabases + ) + ); + $message->addParam($numberOfDatabases); + } + + $json = []; + if ($message instanceof Message) { + $json = ['message' => $message]; + $this->response->setRequestStatus($message->isSuccess()); + } + + $this->response->addJSON($json); + } +} diff --git a/libraries/classes/Controllers/Server/DatabasesController.php b/libraries/classes/Controllers/Server/DatabasesController.php index 9069110373..ad5d353971 100644 --- a/libraries/classes/Controllers/Server/DatabasesController.php +++ b/libraries/classes/Controllers/Server/DatabasesController.php @@ -10,8 +10,6 @@ use PhpMyAdmin\Charsets\Collation; use PhpMyAdmin\CheckUserPrivileges; use PhpMyAdmin\Controllers\AbstractController; use PhpMyAdmin\DatabaseInterface; -use PhpMyAdmin\Html\Generator; -use PhpMyAdmin\Message; use PhpMyAdmin\Query\Utilities; use PhpMyAdmin\RelationCleanup; use PhpMyAdmin\ReplicationInfo; @@ -22,14 +20,10 @@ use PhpMyAdmin\Url; use PhpMyAdmin\Util; use function __; -use function _ngettext; -use function array_key_exists; use function array_keys; use function array_search; use function count; -use function explode; use function in_array; -use function mb_strlen; use function mb_strtolower; use function str_contains; use function strlen; @@ -86,7 +80,7 @@ class DatabasesController extends AbstractController $checkUserPrivileges->getPrivileges(); } - public function index(): void + public function __invoke(): void { global $cfg, $server, $dblist, $is_create_db_priv; global $db_to_create, $text_dir, $errorUrl; @@ -187,164 +181,6 @@ class DatabasesController extends AbstractController ]); } - public function create(): void - { - global $cfg, $db; - - $params = [ - 'new_db' => $_POST['new_db'] ?? null, - 'db_collation' => $_POST['db_collation'] ?? null, - ]; - - if (! isset($params['new_db']) || mb_strlen($params['new_db']) === 0 || ! $this->response->isAjax()) { - $this->response->addJSON(['message' => Message::error()]); - - return; - } - - // lower_case_table_names=1 `DB` becomes `db` - if ($this->dbi->getLowerCaseNames() === '1') { - $params['new_db'] = mb_strtolower( - $params['new_db'] - ); - } - - /** - * Builds and executes the db creation sql query - */ - $sqlQuery = 'CREATE DATABASE ' . Util::backquote($params['new_db']); - if (! empty($params['db_collation'])) { - [$databaseCharset] = explode('_', $params['db_collation']); - $charsets = Charsets::getCharsets( - $this->dbi, - $cfg['Server']['DisableIS'] - ); - $collations = Charsets::getCollations( - $this->dbi, - $cfg['Server']['DisableIS'] - ); - if ( - array_key_exists($databaseCharset, $charsets) - && array_key_exists($params['db_collation'], $collations[$databaseCharset]) - ) { - $sqlQuery .= ' DEFAULT' - . Util::getCharsetQueryPart($params['db_collation']); - } - } - - $sqlQuery .= ';'; - - $result = $this->dbi->tryQuery($sqlQuery); - - if (! $result) { - // avoid displaying the not-created db name in header or navi panel - $db = ''; - - $message = Message::rawError((string) $this->dbi->getError()); - $json = ['message' => $message]; - - $this->response->setRequestStatus(false); - } else { - $db = $params['new_db']; - - $message = Message::success(__('Database %1$s has been created.')); - $message->addParam($params['new_db']); - - $scriptName = Util::getScriptNameForOption( - $cfg['DefaultTabDatabase'], - 'database' - ); - - $json = [ - 'message' => $message, - 'sql_query' => Generator::getMessage('', $sqlQuery, 'success'), - 'url' => $scriptName . Url::getCommon( - ['db' => $params['new_db']], - ! str_contains($scriptName, '?') ? '?' : '&' - ), - ]; - } - - $this->response->addJSON($json); - } - - /** - * Handles dropping multiple databases - */ - public function destroy(): void - { - global $selected, $errorUrl, $cfg, $dblist, $reload; - - $params = [ - 'drop_selected_dbs' => $_POST['drop_selected_dbs'] ?? null, - 'selected_dbs' => $_POST['selected_dbs'] ?? null, - ]; - /** @var Message|int $message */ - $message = -1; - - if ( - ! isset($params['drop_selected_dbs']) - || ! $this->response->isAjax() - || (! $this->dbi->isSuperUser() && ! $cfg['AllowUserDropDatabase']) - ) { - $message = Message::error(); - $json = ['message' => $message]; - $this->response->setRequestStatus($message->isSuccess()); - $this->response->addJSON($json); - - return; - } - - if (! isset($params['selected_dbs'])) { - $message = Message::error(__('No databases selected.')); - $json = ['message' => $message]; - $this->response->setRequestStatus($message->isSuccess()); - $this->response->addJSON($json); - - return; - } - - $errorUrl = Url::getFromRoute('/server/databases'); - $selected = $_POST['selected_dbs']; - $rebuildDatabaseList = false; - $sqlQuery = ''; - $numberOfDatabases = count($selected); - - for ($i = 0; $i < $numberOfDatabases; $i++) { - $this->relationCleanup->database($selected[$i]); - $aQuery = 'DROP DATABASE ' . Util::backquote($selected[$i]); - $reload = true; - $rebuildDatabaseList = true; - - $sqlQuery .= $aQuery . ';' . "\n"; - $this->dbi->query($aQuery); - $this->transformations->clear($selected[$i]); - } - - if ($rebuildDatabaseList) { - $dblist->databases->build(); - } - - if ($message === -1) { // no error message - $message = Message::success( - _ngettext( - '%1$d database has been dropped successfully.', - '%1$d databases have been dropped successfully.', - $numberOfDatabases - ) - ); - $message->addParam($numberOfDatabases); - } - - $json = []; - if ($message instanceof Message) { - $json = ['message' => $message]; - $this->response->setRequestStatus($message->isSuccess()); - } - - $this->response->addJSON($json); - } - /** * Extracts parameters sort order and sort by * diff --git a/libraries/routes.php b/libraries/routes.php index 3acb0f8633..6a3f993d0d 100644 --- a/libraries/routes.php +++ b/libraries/routes.php @@ -20,6 +20,7 @@ use PhpMyAdmin\Controllers\Database\ImportController as DatabaseImportController use PhpMyAdmin\Controllers\Database\MultiTableQuery\QueryController; use PhpMyAdmin\Controllers\Database\MultiTableQuery\TablesController as MultiTableQueryTablesController; use PhpMyAdmin\Controllers\Database\MultiTableQueryController; +use PhpMyAdmin\Controllers\Database\Operations\CollationController; use PhpMyAdmin\Controllers\Database\OperationsController; use PhpMyAdmin\Controllers\Database\QueryByExampleController; use PhpMyAdmin\Controllers\Database\RoutinesController; @@ -27,11 +28,13 @@ use PhpMyAdmin\Controllers\Database\SearchController; use PhpMyAdmin\Controllers\Database\SqlAutoCompleteController; use PhpMyAdmin\Controllers\Database\SqlController as DatabaseSqlController; use PhpMyAdmin\Controllers\Database\SqlFormatController; +use PhpMyAdmin\Controllers\Database\Structure; use PhpMyAdmin\Controllers\Database\StructureController; use PhpMyAdmin\Controllers\Database\TrackingController; use PhpMyAdmin\Controllers\Database\TriggersController; use PhpMyAdmin\Controllers\DatabaseController; use PhpMyAdmin\Controllers\ErrorReportController; +use PhpMyAdmin\Controllers\Export\CheckTimeOutController; use PhpMyAdmin\Controllers\Export\ExportController; use PhpMyAdmin\Controllers\Export\TablesController; use PhpMyAdmin\Controllers\Export\Template\CreateController as TemplateCreateController; @@ -61,6 +64,8 @@ use PhpMyAdmin\Controllers\RecentTablesListController; use PhpMyAdmin\Controllers\SchemaExportController; use PhpMyAdmin\Controllers\Server\BinlogController; use PhpMyAdmin\Controllers\Server\CollationsController; +use PhpMyAdmin\Controllers\Server\Databases\CreateController as DatabasesCreateController; +use PhpMyAdmin\Controllers\Server\Databases\DestroyController; use PhpMyAdmin\Controllers\Server\DatabasesController; use PhpMyAdmin\Controllers\Server\EnginesController; use PhpMyAdmin\Controllers\Server\ExportController as ServerExportController; @@ -145,8 +150,8 @@ return static function (RouteCollector $routes): void { $routes->post('/query', QueryController::class); }); $routes->addGroup('/operations', static function (RouteCollector $routes): void { - $routes->addRoute(['GET', 'POST'], '', [OperationsController::class, 'index']); - $routes->post('/collation', [OperationsController::class, 'collation']); + $routes->addRoute(['GET', 'POST'], '', OperationsController::class); + $routes->post('/collation', CollationController::class); }); $routes->addRoute(['GET', 'POST'], '/qbe', QueryByExampleController::class); $routes->addRoute(['GET', 'POST'], '/routines', RoutinesController::class); @@ -157,33 +162,24 @@ return static function (RouteCollector $routes): void { $routes->post('/format', SqlFormatController::class); }); $routes->addGroup('/structure', static function (RouteCollector $routes): void { - $routes->addRoute(['GET', 'POST'], '', [StructureController::class, 'index']); - $routes->post('/add-prefix', [StructureController::class, 'addPrefix']); - $routes->post('/add-prefix-table', [StructureController::class, 'addPrefixTable']); - $routes->post('/central-columns-add', [StructureController::class, 'centralColumnsAdd']); - $routes->post('/central-columns-make-consistent', [ - StructureController::class, - 'centralColumnsMakeConsistent', - ]); - $routes->post('/central-columns-remove', [StructureController::class, 'centralColumnsRemove']); - $routes->post('/change-prefix-form', [StructureController::class, 'changePrefixForm']); - $routes->post('/copy-form', [StructureController::class, 'copyForm']); - $routes->post('/copy-table', [StructureController::class, 'copyTable']); - $routes->post('/copy-table-with-prefix', [StructureController::class, 'copyTableWithPrefix']); - $routes->post('/drop-form', [StructureController::class, 'dropForm']); - $routes->post('/drop-table', [StructureController::class, 'dropTable']); - $routes->post('/empty-form', [StructureController::class, 'emptyForm']); - $routes->post('/empty-table', [StructureController::class, 'emptyTable']); - $routes->addRoute(['GET', 'POST'], '/favorite-table', [ - StructureController::class, - 'addRemoveFavoriteTablesAction', - ]); - $routes->addRoute(['GET', 'POST'], '/real-row-count', [ - StructureController::class, - 'handleRealRowCountRequestAction', - ]); - $routes->post('/replace-prefix', [StructureController::class, 'replacePrefix']); - $routes->post('/show-create', [StructureController::class, 'showCreate']); + $routes->addRoute(['GET', 'POST'], '', StructureController::class); + $routes->post('/add-prefix', Structure\AddPrefixController::class); + $routes->post('/add-prefix-table', Structure\AddPrefixTableController::class); + $routes->post('/central-columns-add', Structure\CentralColumns\AddController::class); + $routes->post('/central-columns-make-consistent', Structure\CentralColumns\MakeConsistentController::class); + $routes->post('/central-columns-remove', Structure\CentralColumns\RemoveController::class); + $routes->post('/change-prefix-form', Structure\ChangePrefixFormController::class); + $routes->post('/copy-form', Structure\CopyFormController::class); + $routes->post('/copy-table', Structure\CopyTableController::class); + $routes->post('/copy-table-with-prefix', Structure\CopyTableWithPrefixController::class); + $routes->post('/drop-form', Structure\DropFormController::class); + $routes->post('/drop-table', Structure\DropTableController::class); + $routes->post('/empty-form', Structure\EmptyFormController::class); + $routes->post('/empty-table', Structure\EmptyTableController::class); + $routes->addRoute(['GET', 'POST'], '/favorite-table', Structure\FavoriteTableController::class); + $routes->addRoute(['GET', 'POST'], '/real-row-count', Structure\RealRowCountController::class); + $routes->post('/replace-prefix', Structure\ReplacePrefixController::class); + $routes->post('/show-create', Structure\ShowCreateController::class); }); $routes->addRoute(['GET', 'POST'], '/tracking', TrackingController::class); $routes->addRoute(['GET', 'POST'], '/triggers', TriggersController::class); @@ -192,7 +188,7 @@ return static function (RouteCollector $routes): void { $routes->addRoute(['GET', 'POST'], '/error-report', ErrorReportController::class); $routes->addGroup('/export', static function (RouteCollector $routes): void { $routes->addRoute(['GET', 'POST'], '', ExportController::class); - $routes->get('/check-time-out', [ExportController::class, 'checkTimeOut']); + $routes->get('/check-time-out', CheckTimeOutController::class); $routes->post('/tables', TablesController::class); $routes->addGroup('/template', static function (RouteCollector $routes): void { $routes->post('/create', TemplateCreateController::class); @@ -227,9 +223,9 @@ return static function (RouteCollector $routes): void { $routes->addRoute(['GET', 'POST'], '/binlog', BinlogController::class); $routes->get('/collations', CollationsController::class); $routes->addGroup('/databases', static function (RouteCollector $routes): void { - $routes->addRoute(['GET', 'POST'], '', [DatabasesController::class, 'index']); - $routes->post('/create', [DatabasesController::class, 'create']); - $routes->post('/destroy', [DatabasesController::class, 'destroy']); + $routes->addRoute(['GET', 'POST'], '', DatabasesController::class); + $routes->post('/create', DatabasesCreateController::class); + $routes->post('/destroy', DestroyController::class); }); $routes->addGroup('/engines', static function (RouteCollector $routes): void { $routes->get('', [EnginesController::class, 'index']); diff --git a/libraries/services_controllers.php b/libraries/services_controllers.php index b6ae3c6ead..1d23024e1e 100644 --- a/libraries/services_controllers.php +++ b/libraries/services_controllers.php @@ -152,6 +152,16 @@ return [ '$dbi' => '@dbi', ], ], + PhpMyAdmin\Controllers\Database\Operations\CollationController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Operations\CollationController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$operations' => '@operations', + '$dbi' => '@dbi', + ], + ], PhpMyAdmin\Controllers\Database\OperationsController::class => [ 'class' => PhpMyAdmin\Controllers\Database\OperationsController::class, 'arguments' => [ @@ -230,6 +240,168 @@ return [ '$db' => '%db%', ], ], + PhpMyAdmin\Controllers\Database\Structure\AddPrefixController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\AddPrefixController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\AddPrefixTableController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\AddPrefixTableController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\CentralColumns\AddController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\CentralColumns\AddController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\CentralColumns\MakeConsistentController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\CentralColumns\MakeConsistentController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\CentralColumns\RemoveController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\CentralColumns\RemoveController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\ChangePrefixFormController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\ChangePrefixFormController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\CopyFormController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\CopyFormController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\CopyTableController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\CopyTableController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$operations' => '@operations', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\CopyTableWithPrefixController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\CopyTableWithPrefixController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\DropFormController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\DropFormController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\DropTableController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\DropTableController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$relationCleanup' => '@relation_cleanup', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\EmptyFormController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\EmptyFormController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\EmptyTableController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\EmptyTableController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$relation' => '@relation', + '$relationCleanup' => '@relation_cleanup', + '$operations' => '@operations', + '$flash' => '@flash', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\FavoriteTableController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\FavoriteTableController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$relation' => '@relation', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\RealRowCountController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\RealRowCountController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + ], + ], + PhpMyAdmin\Controllers\Database\Structure\ReplacePrefixController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\ReplacePrefixController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + '$structureController' => '@' . PhpMyAdmin\Controllers\Database\StructureController::class, + ], + ], + PhpMyAdmin\Controllers\Database\Structure\ShowCreateController::class => [ + 'class' => PhpMyAdmin\Controllers\Database\Structure\ShowCreateController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$db' => '%db%', + '$dbi' => '@dbi', + ], + ], PhpMyAdmin\Controllers\Database\StructureController::class => [ 'class' => PhpMyAdmin\Controllers\Database\StructureController::class, 'arguments' => [ @@ -279,6 +451,13 @@ return [ '$errorHandler' => '@error_handler', ], ], + PhpMyAdmin\Controllers\Export\CheckTimeOutController::class => [ + 'class' => PhpMyAdmin\Controllers\Export\CheckTimeOutController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + ], + ], PhpMyAdmin\Controllers\Export\ExportController::class => [ 'class' => PhpMyAdmin\Controllers\Export\ExportController::class, 'arguments' => [ @@ -523,6 +702,24 @@ return [ '$dbi' => '@dbi', ], ], + PhpMyAdmin\Controllers\Server\Databases\CreateController::class => [ + 'class' => PhpMyAdmin\Controllers\Server\Databases\CreateController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$dbi' => '@dbi', + ], + ], + PhpMyAdmin\Controllers\Server\Databases\DestroyController::class => [ + 'class' => PhpMyAdmin\Controllers\Server\Databases\DestroyController::class, + 'arguments' => [ + '$response' => '@response', + '$template' => '@template', + '$dbi' => '@dbi', + '$transformations' => '@transformations', + '$relationCleanup' => '@relation_cleanup', + ], + ], PhpMyAdmin\Controllers\Server\DatabasesController::class => [ 'class' => PhpMyAdmin\Controllers\Server\DatabasesController::class, 'arguments' => [ diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 16b4ff82ae..63360a3ad1 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1352,12 +1352,38 @@ <code>$table</code> </MixedAssignment> </file> - <file src="libraries/classes/Controllers/Database/OperationsController.php"> - <MixedArgument occurrences="17"> - <code>$_POST['comment']</code> + <file src="libraries/classes/Controllers/Database/Operations/CollationController.php"> + <MixedArgument occurrences="7"> <code>$_POST['db_collation']</code> <code>$_POST['db_collation'] ?? ''</code> <code>$_POST['db_collation'] ?? ''</code> + <code>$db</code> + <code>$db</code> + <code>$db</code> + <code>$tableName</code> + </MixedArgument> + <MixedArgumentTypeCoercion occurrences="1"> + <code>['db' => $db]</code> + </MixedArgumentTypeCoercion> + <MixedAssignment occurrences="2"> + <code>$data</code> + <code>$tableName</code> + </MixedAssignment> + <MixedOperand occurrences="3"> + <code>Util::backquote($db)</code> + <code>Util::backquote($db)</code> + <code>Util::backquote($tableName)</code> + </MixedOperand> + <PossiblyNullArgument occurrences="1"> + <code>$_POST['db_collation']</code> + </PossiblyNullArgument> + <UnusedForeachValue occurrences="1"> + <code>$data</code> + </UnusedForeachValue> + </file> + <file src="libraries/classes/Controllers/Database/OperationsController.php"> + <MixedArgument occurrences="10"> + <code>$_POST['comment']</code> <code>$_POST['newname']</code> <code>$_POST['newname']</code> <code>$_POST['newname']</code> @@ -1366,14 +1392,9 @@ <code>$db</code> <code>$db</code> <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> <code>$message</code> - <code>$tableName</code> </MixedArgument> - <MixedArgumentTypeCoercion occurrences="2"> - <code>['db' => $db]</code> + <MixedArgumentTypeCoercion occurrences="1"> <code>['db' => $db]</code> </MixedArgumentTypeCoercion> <MixedArrayAssignment occurrences="1"> @@ -1382,21 +1403,13 @@ <MixedArrayOffset occurrences="1"> <code>$cfg['Servers'][$server]</code> </MixedArrayOffset> - <MixedAssignment occurrences="4"> - <code>$data</code> + <MixedAssignment occurrences="2"> <code>$db</code> <code>$db</code> - <code>$tableName</code> </MixedAssignment> - <MixedOperand occurrences="4"> - <code>Util::backquote($db)</code> - <code>Util::backquote($db)</code> + <MixedOperand occurrences="1"> <code>Util::backquote($db)</code> - <code>Util::backquote($tableName)</code> </MixedOperand> - <PossiblyNullArgument occurrences="1"> - <code>$_POST['db_collation']</code> - </PossiblyNullArgument> <RedundantCondition occurrences="5"> <code>! $_error</code> <code>! $_error</code> @@ -1407,9 +1420,6 @@ <TypeDoesNotContainNull occurrences="1"> <code>''</code> </TypeDoesNotContainNull> - <UnusedForeachValue occurrences="1"> - <code>$data</code> - </UnusedForeachValue> </file> <file src="libraries/classes/Controllers/Database/PrivilegesController.php"> <MixedArgument occurrences="1"> @@ -1505,28 +1515,295 @@ <code>$query</code> </PossiblyNullArgument> </file> - <file src="libraries/classes/Controllers/Database/StructureController.php"> - <InvalidScalarArgument occurrences="2"> - <code>$formattedOverhead</code> - <code>$formattedSize</code> - </InvalidScalarArgument> - <MixedArgument occurrences="89"> + <file src="libraries/classes/Controllers/Database/Structure/AddPrefixController.php"> + <MixedAssignment occurrences="3"> + <code>$params['selected'][]</code> + <code>$selected</code> + <code>$selectedValue</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/AddPrefixTableController.php"> + <MixedArgument occurrences="3"> + <code>$db</code> + <code>$selected</code> + <code>$selected[$i]</code> + </MixedArgument> + <MixedArrayAccess occurrences="1"> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="1"> + <code>$selected</code> + </MixedAssignment> + <MixedOperand occurrences="3"> + <code>$_POST['add_prefix']</code> + <code>Util::backquote($newTableName)</code> + <code>Util::backquote($selected[$i])</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/CentralColumns/AddController.php"> + <MixedArgument occurrences="1"> + <code>$selected</code> + </MixedArgument> + <MixedAssignment occurrences="1"> + <code>$selected</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/CentralColumns/MakeConsistentController.php"> + <MixedArgument occurrences="2"> + <code>$db</code> + <code>$selected</code> + </MixedArgument> + <MixedAssignment occurrences="1"> + <code>$selected</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/CentralColumns/RemoveController.php"> + <MixedArgument occurrences="2"> <code>$_POST['db']</code> - <code>$_POST['master_connection'] ?? null</code> + <code>$selected</code> + </MixedArgument> + <MixedAssignment occurrences="1"> + <code>$selected</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/ChangePrefixFormController.php"> + <MixedAssignment occurrences="4"> + <code>$selected</code> + <code>$selectedValue</code> + <code>$submitMult</code> + <code>$urlParams['selected'][]</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/CopyFormController.php"> + <MixedAssignment occurrences="6"> + <code>$databaseName</code> + <code>$databasesList</code> + <code>$key</code> + <code>$selected</code> + <code>$selectedValue</code> + <code>$urlParams['selected'][]</code> + </MixedAssignment> + <MixedMethodCall occurrences="2"> + <code>getList</code> + <code>offsetUnset</code> + </MixedMethodCall> + <MixedPropertyFetch occurrences="1"> + <code>$dblist->databases</code> + </MixedPropertyFetch> + </file> + <file src="libraries/classes/Controllers/Database/Structure/CopyTableController.php"> + <MixedArgument occurrences="9"> <code>$_POST['what']</code> - <code>$_REQUEST['pos']</code> - <code>$checkTime</code> - <code>$checkTimeAll</code> - <code>$createTime</code> - <code>$createTimeAll</code> + <code>$db</code> + <code>$db</code> + <code>$selected</code> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + <code>$targetDb</code> + </MixedArgument> + <MixedArrayAccess occurrences="1"> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="2"> + <code>$selected</code> + <code>$targetDb</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/CopyTableWithPrefixController.php"> + <MixedArgument occurrences="5"> + <code>$current</code> + <code>$current</code> + <code>$db</code> + <code>$db</code> + <code>$selected</code> + </MixedArgument> + <MixedArrayAccess occurrences="1"> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="4"> <code>$current</code> + <code>$fromPrefix</code> + <code>$selected</code> + <code>$toPrefix</code> + </MixedAssignment> + <MixedOperand occurrences="1"> + <code>$toPrefix</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/DropFormController.php"> + <MixedArgument occurrences="3"> <code>$current</code> <code>$current</code> + <code>$db</code> + </MixedArgument> + <MixedAssignment occurrences="7"> <code>$current</code> <code>$current</code> + <code>$selected</code> + <code>$selectedValue</code> + <code>$selectedValue</code> + <code>$urlParams['selected'][]</code> + <code>$urlParams['views'][]</code> + </MixedAssignment> + <MixedOperand occurrences="2"> + <code>Util::backquote(htmlspecialchars($current))</code> + <code>Util::backquote(htmlspecialchars($current))</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/DropTableController.php"> + <MixedArgument occurrences="5"> + <code>$current</code> + <code>$current</code> + <code>$db</code> + <code>$selected</code> + <code>$selected[$i]</code> + </MixedArgument> + <MixedArrayAccess occurrences="3"> + <code>$_SESSION['tmpval']['table_limit_offset']</code> + <code>$_SESSION['tmpval']['table_limit_offset_db']</code> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="6"> + <code>$current</code> + <code>$multBtn</code> + <code>$reload</code> + <code>$result</code> + <code>$result</code> + <code>$selected</code> + </MixedAssignment> + <MixedOperand occurrences="2"> + <code>Util::backquote($current)</code> + <code>Util::backquote($current)</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/EmptyFormController.php"> + <MixedArgument occurrences="1"> + <code>$selectedValue</code> + </MixedArgument> + <MixedAssignment occurrences="2"> + <code>$selected</code> + <code>$selectedValue</code> + </MixedAssignment> + <MixedOperand occurrences="1"> + <code>Util::backquote(htmlspecialchars($selectedValue))</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/EmptyTableController.php"> + <MixedArgument occurrences="6"> + <code>$_REQUEST['pos']</code> + <code>$db</code> + <code>$db</code> + <code>$selected</code> + <code>$selected[$i]</code> + <code>$table</code> + </MixedArgument> + <MixedArrayAccess occurrences="1"> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="2"> + <code>$multBtn</code> + <code>$selected</code> + </MixedAssignment> + <MixedOperand occurrences="1"> + <code>Util::backquote($selected[$i])</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/FavoriteTableController.php"> + <MixedArgument occurrences="5"> + <code>$favoriteTable</code> + <code>$favoriteTables</code> + <code>$parameters['favoriteTables']</code> + <code>$value['db']</code> + <code>$value['table']</code> + </MixedArgument> + <MixedArgumentTypeCoercion occurrences="1"> + <code>['db' => $db]</code> + </MixedArgumentTypeCoercion> + <MixedArrayAccess occurrences="5"> + <code>$_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']]</code> + <code>$value['db']</code> + <code>$value['db']</code> + <code>$value['table']</code> + <code>$value['table']</code> + </MixedArrayAccess> + <MixedArrayAssignment occurrences="2"> + <code>$_SESSION['tmpval']['favorites_synced']</code> + <code>$favoriteTables[$user]</code> + </MixedArrayAssignment> + <MixedAssignment occurrences="6"> + <code>$favoriteTable</code> + <code>$favoriteTables</code> + <code>$favoriteTables</code> + <code>$key</code> + <code>$value</code> + <code>$value</code> + </MixedAssignment> + <UnusedVariable occurrences="1"> + <code>$key</code> + </UnusedVariable> + </file> + <file src="libraries/classes/Controllers/Database/Structure/RealRowCountController.php"> + <MixedArgument occurrences="1"> + <code>$table['TABLE_NAME']</code> + </MixedArgument> + <MixedArgumentTypeCoercion occurrences="1"> + <code>['db' => $db]</code> + </MixedArgumentTypeCoercion> + <MixedArrayAccess occurrences="2"> + <code>$table['TABLE_NAME']</code> + <code>$table['TABLE_NAME']</code> + </MixedArrayAccess> + <MixedAssignment occurrences="1"> + <code>$table</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/Structure/ReplacePrefixController.php"> + <MixedArgument occurrences="6"> <code>$current</code> <code>$current</code> + <code>$db</code> + <code>$newTableName</code> + <code>$selected</code> + <code>$selected[$i]</code> + </MixedArgument> + <MixedArrayAccess occurrences="1"> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="5"> <code>$current</code> + <code>$fromPrefix</code> + <code>$newTableName</code> + <code>$selected</code> + <code>$toPrefix</code> + </MixedAssignment> + <MixedOperand occurrences="3"> + <code>$toPrefix</code> + <code>Util::backquote($newTableName)</code> + <code>Util::backquote($selected[$i])</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Database/Structure/ShowCreateController.php"> + <MixedArgument occurrences="2"> + <code>$object->showCreate()</code> + <code>$selected</code> + </MixedArgument> + <MixedAssignment occurrences="1"> + <code>$selected</code> + </MixedAssignment> + </file> + <file src="libraries/classes/Controllers/Database/StructureController.php"> + <InvalidScalarArgument occurrences="2"> + <code>$formattedOverhead</code> + <code>$formattedSize</code> + </InvalidScalarArgument> + <MixedArgument occurrences="38"> + <code>$_POST['master_connection'] ?? null</code> + <code>$checkTime</code> + <code>$checkTimeAll</code> + <code>$createTime</code> + <code>$createTimeAll</code> <code>$currentTable</code> <code>$currentTable</code> <code>$currentTable['Collation']</code> @@ -1543,25 +1820,9 @@ <code>$currentTable['TABLE_NAME']</code> <code>$currentTable['TABLE_NAME']</code> <code>$currentTable['TABLE_NAME']</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> - <code>$db</code> <code>$dbTable</code> <code>$dbTable</code> - <code>$favoriteTable</code> - <code>$favoriteTables</code> - <code>$newTableName</code> - <code>$object->showCreate()</code> <code>$overheadSize</code> - <code>$parameters['favoriteTables']</code> <code>$replicaInfo['Do_DB']</code> <code>$replicaInfo['Do_DB']</code> <code>$replicaInfo['Do_DB']</code> @@ -1570,45 +1831,17 @@ <code>$replicaInfo['Ignore_Table']</code> <code>$replicaInfo['Wild_Do_Table']</code> <code>$replicaInfo['Wild_Ignore_Table']</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selectedValue</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> <code>$sumSize</code> - <code>$table</code> <code>$tableIsView</code> - <code>$table['TABLE_NAME']</code> - <code>$targetDb</code> <code>$truename</code> <code>$updateTime</code> <code>$updateTimeAll</code> - <code>$value['db']</code> - <code>$value['table']</code> </MixedArgument> - <MixedArgumentTypeCoercion occurrences="3"> - <code>['db' => $db]</code> - <code>['db' => $db]</code> + <MixedArgumentTypeCoercion occurrences="1"> <code>['db' => $db]</code> </MixedArgumentTypeCoercion> - <MixedArrayAccess occurrences="31"> + <MixedArrayAccess occurrences="19"> <code>$_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']]</code> - <code>$_SESSION['tmpval']['table_limit_offset']</code> - <code>$_SESSION['tmpval']['table_limit_offset_db']</code> <code>$currentTable['Check_time']</code> <code>$currentTable['Collation']</code> <code>$currentTable['Create_time']</code> @@ -1625,135 +1858,54 @@ <code>$currentTable['TABLE_ROWS']</code> <code>$currentTable['TABLE_TYPE']</code> <code>$currentTable['Update_time']</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$table['TABLE_NAME']</code> - <code>$table['TABLE_NAME']</code> <code>$value['db']</code> - <code>$value['db']</code> - <code>$value['table']</code> <code>$value['table']</code> </MixedArrayAccess> - <MixedArrayAssignment occurrences="3"> - <code>$_SESSION['tmpval']['favorites_synced']</code> + <MixedArrayAssignment occurrences="1"> <code>$currentTable['TABLE_ROWS']</code> - <code>$favoriteTables[$user]</code> </MixedArrayAssignment> - <MixedAssignment occurrences="80"> + <MixedAssignment occurrences="29"> <code>$charset</code> <code>$checkTime</code> <code>$checkTimeAll</code> <code>$createTime</code> <code>$createTimeAll</code> - <code>$current</code> - <code>$current</code> - <code>$current</code> - <code>$current</code> - <code>$current</code> <code>$currentTable</code> <code>$currentTable['Rows']</code> <code>$currentTable['TABLE_ROWS']</code> <code>$currentTable['TABLE_ROWS']</code> - <code>$databaseName</code> - <code>$databasesList</code> <code>$dbTable</code> - <code>$favoriteTable</code> - <code>$favoriteTables</code> <code>$favoriteTables</code> - <code>$fromPrefix</code> - <code>$fromPrefix</code> - <code>$key</code> - <code>$key</code> - <code>$multBtn</code> - <code>$multBtn</code> - <code>$newTableName</code> <code>$overheadSize</code> - <code>$params['selected'][]</code> - <code>$reload</code> - <code>$result</code> - <code>$result</code> <code>$searchDb</code> <code>$searchDoDBInDB</code> <code>$searchDoDBInTruename</code> <code>$searchTable</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selected</code> - <code>$selectedValue</code> - <code>$selectedValue</code> - <code>$selectedValue</code> - <code>$selectedValue</code> - <code>$selectedValue</code> - <code>$selectedValue</code> - <code>$submitMult</code> <code>$sumEntries</code> - <code>$table</code> - <code>$targetDb</code> <code>$this->dbIsSystemSchema</code> <code>$this->isShowStats</code> <code>$this->numTables</code> <code>$this->position</code> <code>$this->tables</code> <code>$this->totalNumTables</code> - <code>$toPrefix</code> - <code>$toPrefix</code> <code>$truename</code> <code>$updateTime</code> <code>$updateTimeAll</code> - <code>$urlParams['selected'][]</code> - <code>$urlParams['selected'][]</code> - <code>$urlParams['selected'][]</code> <code>$urlParams['sort']</code> <code>$urlParams['sort_order']</code> - <code>$urlParams['views'][]</code> - <code>$value</code> <code>$value</code> </MixedAssignment> - <MixedMethodCall occurrences="3"> + <MixedMethodCall occurrences="1"> <code>getCharset</code> - <code>getList</code> - <code>offsetUnset</code> </MixedMethodCall> - <MixedOperand occurrences="20"> - <code>$_POST['add_prefix']</code> + <MixedOperand occurrences="7"> <code>$currentTable['Data_free']</code> <code>$currentTable['Data_length']</code> <code>$currentTable['Data_length']</code> <code>$currentTable['Data_length']</code> <code>$currentTable['TABLE_NAME']</code> <code>$currentTable['TABLE_ROWS']</code> - <code>$toPrefix</code> - <code>$toPrefix</code> - <code>Util::backquote($current)</code> - <code>Util::backquote($current)</code> - <code>Util::backquote($newTableName)</code> - <code>Util::backquote($newTableName)</code> - <code>Util::backquote($selected[$i])</code> - <code>Util::backquote($selected[$i])</code> - <code>Util::backquote($selected[$i])</code> - <code>Util::backquote(htmlspecialchars($current))</code> - <code>Util::backquote(htmlspecialchars($current))</code> - <code>Util::backquote(htmlspecialchars($selectedValue))</code> </MixedOperand> - <MixedPropertyFetch occurrences="1"> - <code>$dblist->databases</code> - </MixedPropertyFetch> <PossiblyNullArrayAccess occurrences="8"> <code>$formattedOverhead</code> <code>$formattedSize</code> @@ -1772,8 +1924,7 @@ <code>$tables</code> <code>$totalNumTables</code> </PropertyNotSetInConstructor> - <UnusedVariable occurrences="2"> - <code>$key</code> + <UnusedVariable occurrences="1"> <code>$keyname</code> </UnusedVariable> </file> @@ -2369,19 +2520,62 @@ <code>$this->collations[$charset->getName()]</code> </PossiblyNullIterator> </file> - <file src="libraries/classes/Controllers/Server/DatabasesController.php"> - <MixedArgument occurrences="23"> - <code>$_POST['master_connection'] ?? null</code> + <file src="libraries/classes/Controllers/Server/Databases/CreateController.php"> + <MixedArgument occurrences="7"> <code>$collations[$databaseCharset]</code> - <code>$database['DEFAULT_COLLATION_NAME']</code> - <code>$database['SCHEMA_NAME']</code> - <code>$dblist->databases</code> <code>$params['db_collation']</code> <code>$params['db_collation']</code> <code>$params['db_collation']</code> <code>$params['new_db']</code> <code>$params['new_db']</code> <code>$params['new_db']</code> + </MixedArgument> + <MixedArgumentTypeCoercion occurrences="1"> + <code>['db' => $params['new_db']]</code> + </MixedArgumentTypeCoercion> + <MixedAssignment occurrences="2"> + <code>$db</code> + <code>$result</code> + </MixedAssignment> + <MixedOperand occurrences="1"> + <code>Util::backquote($params['new_db'])</code> + </MixedOperand> + </file> + <file src="libraries/classes/Controllers/Server/Databases/DestroyController.php"> + <MixedArgument occurrences="4"> + <code>$selected</code> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + </MixedArgument> + <MixedArrayAccess occurrences="3"> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + <code>$selected[$i]</code> + </MixedArrayAccess> + <MixedAssignment occurrences="1"> + <code>$selected</code> + </MixedAssignment> + <MixedMethodCall occurrences="1"> + <code>build</code> + </MixedMethodCall> + <MixedOperand occurrences="1"> + <code>Util::backquote($selected[$i])</code> + </MixedOperand> + <MixedPropertyFetch occurrences="1"> + <code>$dblist->databases</code> + </MixedPropertyFetch> + <UnusedVariable occurrences="2"> + <code>$sqlQuery</code> + <code>$sqlQuery</code> + </UnusedVariable> + </file> + <file src="libraries/classes/Controllers/Server/DatabasesController.php"> + <MixedArgument occurrences="12"> + <code>$_POST['master_connection'] ?? null</code> + <code>$database['DEFAULT_COLLATION_NAME']</code> + <code>$database['SCHEMA_NAME']</code> + <code>$dblist->databases</code> <code>$params['sort_by']</code> <code>$params['sort_order']</code> <code>$primaryInfo['Do_DB']</code> @@ -2390,16 +2584,11 @@ <code>$replicaInfo['Do_DB']</code> <code>$replicaInfo['Do_DB']</code> <code>$replicaInfo['Ignore_DB']</code> - <code>$selected</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> </MixedArgument> - <MixedArgumentTypeCoercion occurrences="2"> + <MixedArgumentTypeCoercion occurrences="1"> <code>['db' => $database['SCHEMA_NAME']]</code> - <code>['db' => $params['new_db']]</code> </MixedArgumentTypeCoercion> - <MixedArrayAccess occurrences="16"> + <MixedArrayAccess occurrences="13"> <code>$database[$key]</code> <code>$database['DEFAULT_COLLATION_NAME']</code> <code>$database['SCHEMA_NAME']</code> @@ -2412,9 +2601,6 @@ <code>$database['SCHEMA_NAME']</code> <code>$database['SCHEMA_NAME']</code> <code>$database['SCHEMA_NAME']</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> - <code>$selected[$i]</code> <code>$totalStatistics[$key]['raw']</code> </MixedArrayAccess> <MixedArrayAssignment occurrences="2"> @@ -2425,28 +2611,19 @@ <code>$databases[$database['SCHEMA_NAME']]</code> <code>$databases[$database['SCHEMA_NAME']]</code> </MixedArrayOffset> - <MixedAssignment occurrences="10"> + <MixedAssignment occurrences="7"> <code>$database</code> - <code>$db</code> <code>$key</code> <code>$key</code> <code>$key</code> <code>$key</code> - <code>$result</code> - <code>$selected</code> <code>$statistics[$key]['raw']</code> <code>$totalStatistics[$key]['raw']</code> </MixedAssignment> - <MixedMethodCall occurrences="1"> - <code>build</code> - </MixedMethodCall> - <MixedOperand occurrences="3"> + <MixedOperand occurrences="1"> <code>$totalStatistics[$key]['raw']</code> - <code>Util::backquote($params['new_db'])</code> - <code>Util::backquote($selected[$i])</code> </MixedOperand> - <MixedPropertyFetch occurrences="2"> - <code>$dblist->databases</code> + <MixedPropertyFetch occurrences="1"> <code>$dblist->databases</code> </MixedPropertyFetch> <PropertyNotSetInConstructor occurrences="4"> @@ -2461,10 +2638,6 @@ <TypeDoesNotContainNull occurrences="1"> <code>0</code> </TypeDoesNotContainNull> - <UnusedVariable occurrences="2"> - <code>$sqlQuery</code> - <code>$sqlQuery</code> - </UnusedVariable> </file> <file src="libraries/classes/Controllers/Server/EnginesController.php"> <MixedArgument occurrences="3"> @@ -17206,14 +17379,24 @@ <code>include $this->object->defaultSource</code> </UnresolvableInclude> </file> + <file src="test/classes/Controllers/Database/Structure/FavoriteTableControllerTest.php"> + <MixedArgument occurrences="1"> + <code>$json</code> + </MixedArgument> + <MixedArrayAccess occurrences="1"> + <code>$json['favoriteTables']</code> + </MixedArrayAccess> + <MixedAssignment occurrences="1"> + <code>$json</code> + </MixedAssignment> + </file> <file src="test/classes/Controllers/Database/StructureControllerTest.php"> - <MixedArgument occurrences="4"> + <MixedArgument occurrences="3"> <code>$currentTable</code> - <code>$json</code> <code>$result</code> <code>$result</code> </MixedArgument> - <MixedArrayAccess occurrences="24"> + <MixedArrayAccess occurrences="23"> <code>$currentTable</code> <code>$currentTable</code> <code>$currentTable</code> @@ -17229,7 +17412,6 @@ <code>$currentTable['Data_free']</code> <code>$currentTable['Rows']</code> <code>$currentTable['TABLE_ROWS']</code> - <code>$json['favoriteTables']</code> <code>$overheadSize</code> <code>$overheadSize</code> <code>$sumSize</code> @@ -17245,8 +17427,7 @@ <code>$currentTable['ENGINE']</code> <code>$currentTable['ENGINE']</code> </MixedArrayAssignment> - <MixedAssignment occurrences="11"> - <code>$json</code> + <MixedAssignment occurrences="10"> <code>$result</code> <code>$result</code> <code>[$currentTable, , , $sumSize]</code> @@ -17279,11 +17460,15 @@ <code>$responseMessage</code> </MixedAssignment> </file> - <file src="test/classes/Controllers/Server/DatabasesControllerTest.php"> - <MixedArgument occurrences="5"> + <file src="test/classes/Controllers/Server/Databases/CreateControllerTest.php"> + <MixedArgument occurrences="3"> <code>$actual['message']</code> <code>$actual['message']</code> <code>$actual['message']</code> + </MixedArgument> + </file> + <file src="test/classes/Controllers/Server/Databases/DestroyControllerTest.php"> + <MixedArgument occurrences="2"> <code>$actual['message']</code> <code>$actual['message']</code> </MixedArgument> diff --git a/test/classes/Controllers/Database/Structure/FavoriteTableControllerTest.php b/test/classes/Controllers/Database/Structure/FavoriteTableControllerTest.php new file mode 100644 index 0000000000..31eb2229f4 --- /dev/null +++ b/test/classes/Controllers/Database/Structure/FavoriteTableControllerTest.php @@ -0,0 +1,63 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Tests\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\Structure\FavoriteTableController; +use PhpMyAdmin\RecentFavoriteTable; +use PhpMyAdmin\Relation; +use PhpMyAdmin\Template; +use PhpMyAdmin\Tests\AbstractTestCase; +use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseStub; +use ReflectionClass; + +use function json_encode; + +/** + * @covers \PhpMyAdmin\Controllers\Database\Structure\FavoriteTableController + */ +class FavoriteTableControllerTest extends AbstractTestCase +{ + public function testSynchronizeFavoriteTables(): void + { + $GLOBALS['server'] = 1; + $GLOBALS['text_dir'] = 'ltr'; + $GLOBALS['PMA_PHP_SELF'] = 'index.php'; + + $favoriteInstance = $this->getMockBuilder(RecentFavoriteTable::class) + ->disableOriginalConstructor() + ->getMock(); + $favoriteInstance->expects($this->exactly(2)) + ->method('getTables') + ->will($this->onConsecutiveCalls([[]], [['db' => 'db', 'table' => 'table']])); + + $class = new ReflectionClass(FavoriteTableController::class); + $method = $class->getMethod('synchronizeFavoriteTables'); + $method->setAccessible(true); + $template = new Template(); + + $controller = new FavoriteTableController( + new ResponseStub(), + $template, + 'db', + new Relation($this->dbi, $template) + ); + + // The user hash for test + $user = 'abcdefg'; + $favoriteTable = [ + $user => [ + [ + 'db' => 'db', + 'table' => 'table', + ], + ], + ]; + + $json = $method->invokeArgs($controller, [$favoriteInstance, $user, $favoriteTable]); + + $this->assertEquals(json_encode($favoriteTable), $json['favoriteTables'] ?? ''); + $this->assertArrayHasKey('list', $json); + } +} diff --git a/test/classes/Controllers/Database/Structure/RealRowCountControllerTest.php b/test/classes/Controllers/Database/Structure/RealRowCountControllerTest.php new file mode 100644 index 0000000000..07ddd9ad8d --- /dev/null +++ b/test/classes/Controllers/Database/Structure/RealRowCountControllerTest.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Tests\Controllers\Database\Structure; + +use PhpMyAdmin\Controllers\Database\Structure\RealRowCountController; +use PhpMyAdmin\Template; +use PhpMyAdmin\Tests\AbstractTestCase; +use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseStub; + +use function json_encode; + +/** + * @covers \PhpMyAdmin\Controllers\Database\Structure\RealRowCountController + */ +class RealRowCountControllerTest extends AbstractTestCase +{ + public function testRealRowCount(): void + { + $GLOBALS['server'] = 1; + $GLOBALS['text_dir'] = 'ltr'; + $GLOBALS['PMA_PHP_SELF'] = 'index.php'; + $GLOBALS['cfg']['Server']['DisableIS'] = true; + $GLOBALS['is_db'] = true; + $GLOBALS['db'] = 'world'; + + $response = new ResponseStub(); + $response->setAjax(true); + + $_REQUEST['table'] = 'City'; + + (new RealRowCountController($response, new Template(), 'world', $this->dbi))(); + + $json = $response->getJSONResult(); + $this->assertEquals('4,079', $json['real_row_count']); + + $_REQUEST['real_row_count_all'] = 'on'; + + (new RealRowCountController($response, new Template(), 'world', $this->dbi))(); + + $json = $response->getJSONResult(); + $expected = [ + ['table' => 'City', 'row_count' => 4079], + ['table' => 'Country', 'row_count' => 239], + ['table' => 'CountryLanguage', 'row_count' => 984], + ]; + $this->assertEquals(json_encode($expected), $json['real_row_count_all']); + } +} diff --git a/test/classes/Controllers/Database/StructureControllerTest.php b/test/classes/Controllers/Database/StructureControllerTest.php index 576a1e7804..5aa1a450a5 100644 --- a/test/classes/Controllers/Database/StructureControllerTest.php +++ b/test/classes/Controllers/Database/StructureControllerTest.php @@ -8,7 +8,6 @@ use PhpMyAdmin\Controllers\Database\StructureController; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\FlashMessages; use PhpMyAdmin\Operations; -use PhpMyAdmin\RecentFavoriteTable; use PhpMyAdmin\Relation; use PhpMyAdmin\RelationCleanup; use PhpMyAdmin\Replication; @@ -16,12 +15,9 @@ use PhpMyAdmin\Table; use PhpMyAdmin\Template; use PhpMyAdmin\Tests\AbstractTestCase; use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseStub; -use PHPUnit\Framework\MockObject\MockObject; use ReflectionClass; use ReflectionException; -use function json_encode; - /** * @covers \PhpMyAdmin\Controllers\Database\StructureController */ @@ -393,113 +389,6 @@ class StructureControllerTest extends AbstractTestCase } /** - * Tests for synchronizeFavoriteTables() - */ - public function testSynchronizeFavoriteTables(): void - { - $favoriteInstance = $this->getFavoriteTablesMock(); - - $class = new ReflectionClass(StructureController::class); - $method = $class->getMethod('synchronizeFavoriteTables'); - $method->setAccessible(true); - - $controller = new StructureController( - $this->response, - $this->template, - $GLOBALS['db'], - $this->relation, - $this->replication, - $this->relationCleanup, - $this->operations, - $GLOBALS['dbi'], - $this->flash - ); - - // The user hash for test - $user = 'abcdefg'; - $favoriteTable = [ - $user => [ - [ - 'db' => 'db', - 'table' => 'table', - ], - ], - ]; - - $json = $method->invokeArgs($controller, [$favoriteInstance, $user, $favoriteTable]); - - $this->assertEquals(json_encode($favoriteTable), $json['favoriteTables'] ?? ''); - $this->assertArrayHasKey('list', $json); - } - - /** - * @return MockObject|RecentFavoriteTable - */ - private function getFavoriteTablesMock() - { - $favoriteInstance = $this->getMockBuilder(RecentFavoriteTable::class) - ->disableOriginalConstructor() - ->getMock(); - $favoriteInstance->expects($this->exactly(2)) - ->method('getTables') - ->will($this->onConsecutiveCalls([[]], [['db' => 'db', 'table' => 'table']])); - - return $favoriteInstance; - } - - /** - * Tests for handleRealRowCountRequestAction() - */ - public function testHandleRealRowCountRequestAction(): void - { - global $is_db; - - $is_db = true; - - $this->response->setAjax(true); - $controller = new StructureController( - $this->response, - $this->template, - $GLOBALS['db'], - $this->relation, - $this->replication, - $this->relationCleanup, - $this->operations, - $GLOBALS['dbi'], - $this->flash - ); - // Showing statistics - $class = new ReflectionClass(StructureController::class); - $property = $class->getProperty('tables'); - $property->setAccessible(true); - - $_REQUEST['table'] = 'table'; - $controller->handleRealRowCountRequestAction(); - $json = $this->response->getJSONResult(); - $this->assertEquals( - 6, - $json['real_row_count'] - ); - - // Fall into another branch - $property->setValue($controller, [['TABLE_NAME' => 'table']]); - $_REQUEST['real_row_count_all'] = 'abc'; - $controller->handleRealRowCountRequestAction(); - $json = $this->response->getJSONResult(); - - $expectedResult = [ - [ - 'table' => 'table', - 'row_count' => 6, - ], - ]; - $this->assertEquals( - json_encode($expectedResult), - $json['real_row_count_all'] - ); - } - - /** * @throws ReflectionException */ public function testDisplayTableList(): void diff --git a/test/classes/Controllers/Server/Databases/CreateControllerTest.php b/test/classes/Controllers/Server/Databases/CreateControllerTest.php new file mode 100644 index 0000000000..53116cfa6a --- /dev/null +++ b/test/classes/Controllers/Server/Databases/CreateControllerTest.php @@ -0,0 +1,61 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Tests\Controllers\Server\Databases; + +use PhpMyAdmin\Controllers\Server\Databases\CreateController; +use PhpMyAdmin\Template; +use PhpMyAdmin\Tests\AbstractTestCase; +use PhpMyAdmin\Tests\Stubs\ResponseRenderer; + +use function __; +use function sprintf; + +/** + * @covers \PhpMyAdmin\Controllers\Server\Databases\CreateController + */ +final class CreateControllerTest extends AbstractTestCase +{ + public function testCreateDatabase(): void + { + $GLOBALS['server'] = 1; + $GLOBALS['text_dir'] = 'ltr'; + $GLOBALS['PMA_PHP_SELF'] = 'index.php'; + $GLOBALS['cfg']['Server']['DisableIS'] = false; + $GLOBALS['db'] = 'pma_test'; + $GLOBALS['table'] = ''; + + $response = new ResponseRenderer(); + $response->setAjax(true); + + $template = new Template(); + $controller = new CreateController($response, $template, $this->dbi); + + $_POST['new_db'] = 'test_db_error'; + + $controller(); + $actual = $response->getJSONResult(); + + $this->assertArrayHasKey('message', $actual); + $this->assertStringContainsString('<div class="alert alert-danger" role="alert">', $actual['message']); + + $response = new ResponseRenderer(); + $response->setAjax(true); + + $controller = new CreateController($response, $template, $this->dbi); + + $_POST['new_db'] = 'test_db'; + $_POST['db_collation'] = 'utf8_general_ci'; + + $controller(); + $actual = $response->getJSONResult(); + + $this->assertArrayHasKey('message', $actual); + $this->assertStringContainsString('<div class="alert alert-success" role="alert">', $actual['message']); + $this->assertStringContainsString( + sprintf(__('Database %1$s has been created.'), 'test_db'), + $actual['message'] + ); + } +} diff --git a/test/classes/Controllers/Server/Databases/DestroyControllerTest.php b/test/classes/Controllers/Server/Databases/DestroyControllerTest.php new file mode 100644 index 0000000000..377d84dc24 --- /dev/null +++ b/test/classes/Controllers/Server/Databases/DestroyControllerTest.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); + +namespace PhpMyAdmin\Tests\Controllers\Server\Databases; + +use PhpMyAdmin\Controllers\Server\Databases\DestroyController; +use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\Relation; +use PhpMyAdmin\RelationCleanup; +use PhpMyAdmin\Template; +use PhpMyAdmin\Tests\AbstractTestCase; +use PhpMyAdmin\Tests\Stubs\ResponseRenderer; +use PhpMyAdmin\Transformations; + +use function __; + +/** + * @covers \PhpMyAdmin\Controllers\Server\Databases\DestroyController + */ +class DestroyControllerTest extends AbstractTestCase +{ + public function testDropDatabases(): void + { + global $cfg; + + $GLOBALS['server'] = 1; + $GLOBALS['text_dir'] = 'ltr'; + $GLOBALS['PMA_PHP_SELF'] = 'index.php'; + + $dbi = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $response = new ResponseRenderer(); + $response->setAjax(true); + + $cfg['AllowUserDropDatabase'] = true; + + $template = new Template(); + $controller = new DestroyController( + $response, + $template, + $dbi, + new Transformations(), + new RelationCleanup($dbi, new Relation($dbi, $template)) + ); + + $_POST['drop_selected_dbs'] = '1'; + + $controller(); + $actual = $response->getJSONResult(); + + $this->assertArrayHasKey('message', $actual); + $this->assertStringContainsString('<div class="alert alert-danger" role="alert">', $actual['message']); + $this->assertStringContainsString(__('No databases selected.'), $actual['message']); + } +} diff --git a/test/classes/Controllers/Server/DatabasesControllerTest.php b/test/classes/Controllers/Server/DatabasesControllerTest.php index 40815e6718..0e60029bd4 100644 --- a/test/classes/Controllers/Server/DatabasesControllerTest.php +++ b/test/classes/Controllers/Server/DatabasesControllerTest.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Controllers\Server; use PhpMyAdmin\Controllers\Server\DatabasesController; -use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Relation; use PhpMyAdmin\RelationCleanup; use PhpMyAdmin\Template; @@ -15,7 +14,6 @@ use PhpMyAdmin\Transformations; use stdClass; use function __; -use function sprintf; /** * @covers \PhpMyAdmin\Controllers\Server\DatabasesController @@ -64,7 +62,7 @@ class DatabasesControllerTest extends AbstractTestCase ); $this->dummyDbi->addSelectDb('mysql'); - $controller->index(); + $controller(); $this->assertAllSelectsConsumed(); $actual = $response->getHTMLResult(); @@ -101,7 +99,7 @@ class DatabasesControllerTest extends AbstractTestCase $_REQUEST['sort_order'] = 'desc'; $this->dummyDbi->addSelectDb('mysql'); - $controller->index(); + $controller(); $this->assertAllSelectsConsumed(); $actual = $response->getHTMLResult(); @@ -117,84 +115,4 @@ class DatabasesControllerTest extends AbstractTestCase $this->assertStringContainsString('MiB', $actual); $this->assertStringContainsString('name="db_collation"', $actual); } - - public function testCreateDatabaseAction(): void - { - $response = new ResponseRenderer(); - $response->setAjax(true); - - $template = new Template(); - $transformations = new Transformations(); - $controller = new DatabasesController( - $response, - $template, - $transformations, - new RelationCleanup($this->dbi, new Relation($this->dbi, $template)), - $this->dbi - ); - - $_POST['new_db'] = 'test_db_error'; - - $controller->create(); - $actual = $response->getJSONResult(); - - $this->assertArrayHasKey('message', $actual); - $this->assertStringContainsString('<div class="alert alert-danger" role="alert">', $actual['message']); - - $response = new ResponseRenderer(); - $response->setAjax(true); - - $controller = new DatabasesController( - $response, - $template, - $transformations, - new RelationCleanup($this->dbi, new Relation($this->dbi, $template)), - $this->dbi - ); - - $_POST['new_db'] = 'test_db'; - $_POST['db_collation'] = 'utf8_general_ci'; - - $controller->create(); - $actual = $response->getJSONResult(); - - $this->assertArrayHasKey('message', $actual); - $this->assertStringContainsString('<div class="alert alert-success" role="alert">', $actual['message']); - $this->assertStringContainsString( - sprintf(__('Database %1$s has been created.'), 'test_db'), - $actual['message'] - ); - } - - public function testDropDatabasesAction(): void - { - global $cfg; - - $dbi = $this->getMockBuilder(DatabaseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $response = new ResponseRenderer(); - $response->setAjax(true); - - $cfg['AllowUserDropDatabase'] = true; - - $template = new Template(); - $controller = new DatabasesController( - $response, - $template, - new Transformations(), - new RelationCleanup($dbi, new Relation($dbi, $template)), - $dbi - ); - - $_POST['drop_selected_dbs'] = '1'; - - $controller->destroy(); - $actual = $response->getJSONResult(); - - $this->assertArrayHasKey('message', $actual); - $this->assertStringContainsString('<div class="alert alert-danger" role="alert">', $actual['message']); - $this->assertStringContainsString(__('No databases selected.'), $actual['message']); - } } diff --git a/test/classes/Stubs/DbiDummy.php b/test/classes/Stubs/DbiDummy.php index eebc63d455..82632fa6cb 100644 --- a/test/classes/Stubs/DbiDummy.php +++ b/test/classes/Stubs/DbiDummy.php @@ -3026,6 +3026,119 @@ class DbiDummy implements DbiExtension ], ], ], + [ + 'query' => 'SHOW TABLE STATUS FROM `world`', + 'columns' => [ + 'Name', + 'Engine', + 'Version', + 'Row_format', + 'Rows', + 'Avg_row_length', + 'Data_length', + 'Max_data_length', + 'Index_length', + 'Data_free', + 'Auto_increment', + 'Create_time', + 'Update_time', + 'Check_time', + 'Collation', + 'Checksum', + 'Create_options', + 'Comment', + 'Max_index_length', + 'Temporary', + ], + 'result' => [ + [ + 'City', + 'InnoDB', + '10', + 'Dynamic', + '4046', + '101', + '409600', + '0', + '114688', + '0', + '4080', + '2020-07-03 17:24:47', + null, + null, + 'utf8mb4_general_ci', + null, + '', + '', + '0', + 'N', + ], + [ + 'Country', + 'InnoDB', + '10', + 'Dynamic', + '239', + '479', + '114688', + '0', + '0', + '0', + null, + '2020-07-03 17:24:47', + null, + null, + 'utf8mb4_general_ci', + null, + '', + '', + '0', + 'N', + ], + [ + 'CountryLanguage', + 'InnoDB', + '10', + 'Dynamic', + '984', + '99', + '98304', + '0', + '65536', + '0', + null, + '2020-07-03 17:24:47', + null, + null, + 'utf8mb4_general_ci', + null, + '', + '', + '0', + 'N', + ], + ], + ], + [ + 'query' => 'SHOW TABLES FROM `world`;', + 'columns' => ['Tables_in_world'], + 'result' => [['City'], ['Country'], ['CountryLanguage']], + ], + [ + 'query' => 'SELECT COUNT(*) AS `row_count` FROM `world`.`City`', + 'columns' => ['row_count'], + 'result' => [['4079']], + ], + [ + 'query' => 'SELECT COUNT(*) AS `row_count` FROM `world`.`Country`', + 'columns' => ['row_count'], + 'result' => [['239']], + ], + [ + 'query' => 'SELECT COUNT(*) AS `row_count` FROM `world`.`CountryLanguage`', + 'columns' => ['row_count'], + 'result' => [['984']], + ], ]; /* Some basic setup for dummy driver */ |