From de3f7bc1dc1fcd7cfd63059176fe63453759a426 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 14 Jan 2025 18:50:21 +0000 Subject: [PATCH] Create a new controller Signed-off-by: Kamil Tekiela --- app/services_controllers.php | 9 +- phpstan-baseline.neon | 69 ++++++++------- psalm-baseline.xml | 29 +++++-- .../Structure/FavoriteTableController.php | 47 ----------- .../SyncFavoriteTablesController.php | 76 +++++++++++++++++ src/Favorites/RecentFavoriteTables.php | 13 +-- src/Routing/Routes.php | 2 + .../Structure/FavoriteTableControllerTest.php | 64 -------------- .../SyncFavoriteTablesControllerTest.php | 83 +++++++++++++++++++ tests/unit/Routing/RoutesTest.php | 2 + 10 files changed, 234 insertions(+), 160 deletions(-) create mode 100644 src/Controllers/SyncFavoriteTablesController.php delete mode 100644 tests/unit/Controllers/Database/Structure/FavoriteTableControllerTest.php create mode 100644 tests/unit/Controllers/SyncFavoriteTablesControllerTest.php diff --git a/app/services_controllers.php b/app/services_controllers.php index f0df786faf..a556997679 100644 --- a/app/services_controllers.php +++ b/app/services_controllers.php @@ -31,6 +31,7 @@ use PhpMyAdmin\Controllers\SchemaExportController; use PhpMyAdmin\Controllers\Server; use PhpMyAdmin\Controllers\Setup; use PhpMyAdmin\Controllers\Sql; +use PhpMyAdmin\Controllers\SyncFavoriteTablesController; use PhpMyAdmin\Controllers\Table; use PhpMyAdmin\Controllers\TableController; use PhpMyAdmin\Controllers\ThemesController; @@ -308,7 +309,6 @@ return [ 'arguments' => [ '$response' => '@response', '$template' => '@template', - '$relation' => '@relation', '$dbTableExists' => '@' . DbTableExists::class, ], ], @@ -1361,5 +1361,12 @@ return [ '$dbTableExists' => '@' . DbTableExists::class, ], ], + SyncFavoriteTablesController::class => [ + 'class' => SyncFavoriteTablesController::class, + 'arguments' => [ + '$response' => '@response', + '$relation' => '@relation', + ], + ], ], ]; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e56e598d3f..d5ad5593aa 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2343,12 +2343,6 @@ parameters: count: 1 path: src/Controllers/Database/Structure/EmptyTableController.php - - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' - identifier: foreach.nonIterable - count: 1 - path: src/Controllers/Database/Structure/FavoriteTableController.php - - message: ''' #^Call to deprecated method getInstance\(\) of class PhpMyAdmin\\Config\: @@ -2358,30 +2352,6 @@ parameters: count: 1 path: src/Controllers/Database/Structure/FavoriteTableController.php - - - message: '#^Cannot access offset ''db'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/Controllers/Database/Structure/FavoriteTableController.php - - - - message: '#^Cannot access offset ''favorites_synced'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/Controllers/Database/Structure/FavoriteTableController.php - - - - message: '#^Cannot access offset ''table'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/Controllers/Database/Structure/FavoriteTableController.php - - - - message: '#^Cannot access offset int\<0, max\> on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: src/Controllers/Database/Structure/FavoriteTableController.php - - message: '#^Loose comparison via "\=\=" is not allowed\.$#' identifier: equal.notAllowed @@ -3657,6 +3627,39 @@ parameters: count: 1 path: src/Controllers/Sql/SqlController.php + - + message: ''' + #^Call to deprecated method getInstance\(\) of class PhpMyAdmin\\Config\: + Use dependency injection instead\.$# + ''' + identifier: staticMethod.deprecated + count: 1 + path: src/Controllers/SyncFavoriteTablesController.php + + - + message: '#^Cannot access offset ''db'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Controllers/SyncFavoriteTablesController.php + + - + message: '#^Cannot access offset ''favorites_synced'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Controllers/SyncFavoriteTablesController.php + + - + message: '#^Cannot access offset ''table'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Controllers/SyncFavoriteTablesController.php + + - + message: '#^Cannot access offset int\<0, max\> on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Controllers/SyncFavoriteTablesController.php + - message: '#^Cannot access offset \(int\|string\) on mixed\.$#' identifier: offsetAccess.nonOffsetAccessible @@ -18819,6 +18822,12 @@ parameters: count: 2 path: tests/unit/Controllers/Sql/SetValuesControllerTest.php + - + message: '#^Parameter \#1 \$str of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:quoteString\(\) expects string, string\|false given\.$#' + identifier: argument.type + count: 1 + path: tests/unit/Controllers/SyncFavoriteTablesControllerTest.php + - message: ''' #^Call to deprecated method getInstance\(\) of class PhpMyAdmin\\Config\: diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 8c35a25eff..d35e31ece0 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1206,21 +1206,15 @@ - - - - - - - - - + + + @@ -2260,6 +2254,23 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Controllers/Database/Structure/FavoriteTableController.php b/src/Controllers/Database/Structure/FavoriteTableController.php index 6c9b0bb74f..3e4a832455 100644 --- a/src/Controllers/Database/Structure/FavoriteTableController.php +++ b/src/Controllers/Database/Structure/FavoriteTableController.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace PhpMyAdmin\Controllers\Database\Structure; use PhpMyAdmin\Config; -use PhpMyAdmin\ConfigStorage\Relation; use PhpMyAdmin\Controllers\InvocableController; use PhpMyAdmin\Current; use PhpMyAdmin\DbTableExists; @@ -33,7 +32,6 @@ final class FavoriteTableController implements InvocableController public function __construct( private readonly ResponseRenderer $response, private readonly Template $template, - private readonly Relation $relation, private readonly DbTableExists $dbTableExists, ) { } @@ -60,20 +58,6 @@ final class FavoriteTableController implements InvocableController // Required to keep each user's preferences separate. $user = sha1($config->selectedServer['user']); - // Request for Synchronization of favorite tables. - if ($request->getParam('sync_favorite_tables') !== null) { - $relationParameters = $this->relation->getRelationParameters(); - if ($relationParameters->favoriteTablesFeature !== null) { - $this->response->addJSON($this->synchronizeFavoriteTables( - $favoriteInstance, - $user, - $favoriteTables, - )); - } - - return $this->response->response(); - } - $databaseName = DatabaseName::tryFrom($request->getParam('db')); if ($databaseName === null || ! $this->dbTableExists->selectDatabase($databaseName)) { $this->response->setRequestStatus(false); @@ -142,35 +126,4 @@ final class FavoriteTableController implements InvocableController return $this->response->response(); } - - /** - * Synchronize favorite tables - * - * @param RecentFavoriteTables $favoriteInstance Instance of this class - * @param string $user The user hash - * @param mixed[] $favoriteTables Existing favorites - * - * @return mixed[] - */ - private function synchronizeFavoriteTables( - RecentFavoriteTables $favoriteInstance, - string $user, - array $favoriteTables, - ): array { - if ($favoriteInstance->getTables() === [] && isset($favoriteTables[$user])) { - foreach ($favoriteTables[$user] as $value) { - $favoriteInstance->add(new RecentFavoriteTable( - DatabaseName::from($value['db']), - TableName::from($value['table']), - )); - } - } - - $favoriteTables[$user] = $favoriteInstance->getTables(); - - // Set flag when localStorage and pmadb(if present) are in sync. - $_SESSION['tmpval']['favorites_synced'][Current::$server] = true; - - return ['favoriteTables' => json_encode($favoriteTables), 'list' => $favoriteInstance->getHtmlList()]; - } } diff --git a/src/Controllers/SyncFavoriteTablesController.php b/src/Controllers/SyncFavoriteTablesController.php new file mode 100644 index 0000000000..dfc4137357 --- /dev/null +++ b/src/Controllers/SyncFavoriteTablesController.php @@ -0,0 +1,76 @@ +isAjax()) { + return $this->response->response(); + } + + $favoriteInstance = RecentFavoriteTables::getInstance(TableType::Favorite); + + $favoriteTables = json_decode($request->getParsedBodyParamAsString('favoriteTables'), true); + if (! is_array($favoriteTables)) { + $favoriteTables = []; + } + + // Required to keep each user's preferences separate. + $user = sha1(Config::getInstance()->selectedServer['user']); + + $relationParameters = $this->relation->getRelationParameters(); + if ($relationParameters->favoriteTablesFeature !== null) { + if ( + $favoriteInstance->getTables() === [] + && isset($favoriteTables[$user]) + && is_array($favoriteTables[$user]) + ) { + foreach ($favoriteTables[$user] as $value) { + $favoriteInstance->add(new RecentFavoriteTable( + DatabaseName::from($value['db']), + TableName::from($value['table']), + )); + } + } + + $favoriteTables[$user] = $favoriteInstance->getTables(); + + // Set flag when localStorage and pmadb(if present) are in sync. + $_SESSION['tmpval']['favorites_synced'][Current::$server] = true; + + $this->response->addJSON([ + 'favoriteTables' => json_encode($favoriteTables), + 'list' => $favoriteInstance->getHtmlList(), + ]); + } + + return $this->response->response(); + } +} diff --git a/src/Favorites/RecentFavoriteTables.php b/src/Favorites/RecentFavoriteTables.php index f452cbb27f..7ba72ac224 100644 --- a/src/Favorites/RecentFavoriteTables.php +++ b/src/Favorites/RecentFavoriteTables.php @@ -294,7 +294,6 @@ class RecentFavoriteTables */ public function getHtmlSyncFavoriteTables(): string { - $retval = ''; if (Current::$server === 0) { return ''; } @@ -305,16 +304,12 @@ class RecentFavoriteTables $relationParameters->favoriteTablesFeature !== null && ! isset($_SESSION['tmpval']['favorites_synced'][Current::$server]) ) { - $url = Url::getFromRoute('/database/structure/favorite-table', [ - 'ajax_request' => true, - 'favorite_table' => true, - 'sync_favorite_tables' => true, - ]); - $retval = ''; + $url = Url::getFromRoute('/sync-favorite-tables', ['ajax_request' => true]); + + return ''; } - return $retval; + return ''; } /** diff --git a/src/Routing/Routes.php b/src/Routing/Routes.php index 0e4f2eae96..91ccba49e6 100644 --- a/src/Routing/Routes.php +++ b/src/Routing/Routes.php @@ -33,6 +33,7 @@ use PhpMyAdmin\Controllers\Preferences; use PhpMyAdmin\Controllers\SchemaExportController; use PhpMyAdmin\Controllers\Server; use PhpMyAdmin\Controllers\Sql; +use PhpMyAdmin\Controllers\SyncFavoriteTablesController; use PhpMyAdmin\Controllers\Table; use PhpMyAdmin\Controllers\TableController; use PhpMyAdmin\Controllers\ThemesController; @@ -314,5 +315,6 @@ final class Routes $routes->addRoute(['GET', 'POST'], '/create', View\CreateController::class); $routes->addRoute(['GET', 'POST'], '/operations', Operations\ViewController::class); }); + $routes->post('/sync-favorite-tables', SyncFavoriteTablesController::class); } } diff --git a/tests/unit/Controllers/Database/Structure/FavoriteTableControllerTest.php b/tests/unit/Controllers/Database/Structure/FavoriteTableControllerTest.php deleted file mode 100644 index 7513ff8b4b..0000000000 --- a/tests/unit/Controllers/Database/Structure/FavoriteTableControllerTest.php +++ /dev/null @@ -1,64 +0,0 @@ -createDbiDummy(); - $dbi = $this->createDatabaseInterface($dbiDummy); - DatabaseInterface::$instance = $dbi; - - $dbiDummy->addResult('SELECT 1 FROM `test_db`.`test_table` LIMIT 1;', [['1']], ['1']); - - (new ReflectionProperty(RecentFavoriteTables::class, 'instances'))->setValue(null, []); - $favoriteInstance = RecentFavoriteTables::getInstance(TableType::Favorite); - - $controller = new FavoriteTableController( - new ResponseStub(), - new Template(), - new Relation($dbi), - new DbTableExists($dbi), - ); - - // The user hash for test - $user = 'abcdefg'; - $favoriteTable = [$user => [['db' => 'test_db', 'table' => 'test_table']]]; - - $_SESSION['tmpval'] = ['favorites_synced' => [Current::$server => null]]; - - $method = new ReflectionMethod(FavoriteTableController::class, 'synchronizeFavoriteTables'); - $json = $method->invokeArgs($controller, [$favoriteInstance, $user, $favoriteTable]); - - self::assertIsArray($json); - self::assertSame(json_encode($favoriteTable), $json['favoriteTables'] ?? ''); - self::assertArrayHasKey('list', $json); - /** - * @psalm-suppress TypeDoesNotContainType - * @phpstan-ignore-next-line - */ - self::assertTrue($_SESSION['tmpval']['favorites_synced'][Current::$server]); - - $dbiDummy->assertAllQueriesConsumed(); - } -} diff --git a/tests/unit/Controllers/SyncFavoriteTablesControllerTest.php b/tests/unit/Controllers/SyncFavoriteTablesControllerTest.php new file mode 100644 index 0000000000..7c083cde0c --- /dev/null +++ b/tests/unit/Controllers/SyncFavoriteTablesControllerTest.php @@ -0,0 +1,83 @@ +createDbiDummy(); + $dbi = $this->createDatabaseInterface($dbiDummy); + DatabaseInterface::$instance = $dbi; + + (new ReflectionProperty(RecentFavoriteTables::class, 'instances'))->setValue(null, []); + + $recentFavoriteTable = new RecentFavoriteTable( + DatabaseName::from('test_db'), + TableName::from('test_table'), + ); + $dbiDummy->addResult('SELECT 1 FROM `test_db`.`test_table` LIMIT 1;', [['1']], ['1']); + $dbiDummy->addResult('SELECT `tables` FROM `pmadb`.`favorite` WHERE `username` = \'root\'', []); + $dbiDummy->addResult("REPLACE INTO `pmadb`.`favorite` (`username`, `tables`) VALUES ('root', " + . $dbi->quoteString(json_encode([$recentFavoriteTable])) . + ')', true); + + $relationParameters = RelationParameters::fromArray([ + 'db' => 'pmadb', + 'favoritework' => 'favoritework', + 'favorite' => 'favorite', + ]); + (new ReflectionProperty(Relation::class, 'cache'))->setValue(null, $relationParameters); + + $response = new ResponseStub(); + $response->setAjax(true); + $controller = new SyncFavoriteTablesController( + $response, + new Relation($dbi), + ); + + // The user hash for test + $user = 'dc76e9f0c0006e8f919e0c515c66dbba3982f785'; + $favoriteTable = json_encode([$user => [['db' => 'test_db', 'table' => 'test_table']]]); + + $_SESSION['tmpval'] = ['favorites_synced' => [Current::$server => null]]; + + $request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/') + ->withHeader('X-Requested-With', 'XMLHttpRequest') + ->withParsedBody(['db' => 'test_db', 'favoriteTables' => $favoriteTable]); + $response = $controller($request); + $json = json_decode((string) $response->getBody(), true); + + self::assertIsArray($json); + self::assertSame($favoriteTable, $json['favoriteTables'] ?? ''); + self::assertArrayHasKey('list', $json); + /** + * @psalm-suppress TypeDoesNotContainType + * @phpstan-ignore-next-line + */ + self::assertTrue($_SESSION['tmpval']['favorites_synced'][Current::$server]); + + $dbiDummy->assertAllQueriesConsumed(); + } +} diff --git a/tests/unit/Routing/RoutesTest.php b/tests/unit/Routing/RoutesTest.php index 88aec2b605..090c5bcefb 100644 --- a/tests/unit/Routing/RoutesTest.php +++ b/tests/unit/Routing/RoutesTest.php @@ -36,6 +36,7 @@ use PhpMyAdmin\Controllers\Preferences; use PhpMyAdmin\Controllers\SchemaExportController; use PhpMyAdmin\Controllers\Server; use PhpMyAdmin\Controllers\Sql; +use PhpMyAdmin\Controllers\SyncFavoriteTablesController; use PhpMyAdmin\Controllers\Table; use PhpMyAdmin\Controllers\TableController; use PhpMyAdmin\Controllers\ThemesController; @@ -323,6 +324,7 @@ final class RoutesTest extends TestCase '/version-check' => VersionCheckController::class, '/view/create' => View\CreateController::class, '/view/operations' => Operations\ViewController::class, + '/sync-favorite-tables' => SyncFavoriteTablesController::class, ]; $regex = '~^(?|/server/engines/([^/]+)|/server/engines/([^/]+)/([^/]+)|/server/variables/get/([^/]+)()())$~';