diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c8d7c05c61..22de22c4de 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1989,12 +1989,36 @@ parameters: count: 1 path: src/Controllers/Database/StructureController.php + - + message: '#^Binary operation "\." between bool\|float\|int\|string and array\\|string\|false results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: src/Controllers/Database/StructureController.php + + - + message: '#^Binary operation "\." between non\-empty\-string and array\\|string\|false results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: src/Controllers/Database/StructureController.php + - message: '#^Call to function array_search\(\) requires parameter \#3 to be set\.$#' identifier: function.strict count: 4 path: src/Controllers/Database/StructureController.php + - + message: '#^Cannot access offset ''table_limit_offset'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 4 + path: src/Controllers/Database/StructureController.php + + - + message: '#^Cannot access offset ''table_limit_offset_db'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Controllers/Database/StructureController.php + - message: '#^Loose comparison via "\!\=" is not allowed\.$#' identifier: notEqual.notAllowed @@ -2007,6 +2031,12 @@ parameters: count: 1 path: src/Controllers/Database/StructureController.php + - + message: '#^Method PhpMyAdmin\\Controllers\\Database\\StructureController\:\:getTableListPosition\(\) should return int but returns mixed\.$#' + identifier: return.type + count: 1 + path: src/Controllers/Database/StructureController.php + - message: '#^Method PhpMyAdmin\\Controllers\\Database\\StructureController\:\:getValuesForAriaTable\(\) should return array\{array\, string, string, string, string, float\|int, int\} but returns array\{array\, string, string, string, string, mixed, int\}\.$#' identifier: return.type @@ -2043,6 +2073,12 @@ parameters: count: 3 path: src/Controllers/Database/StructureController.php + - + message: '#^Parameter \#1 \$str of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:escapeMysqlWildcards\(\) expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Controllers/Database/StructureController.php + - message: '#^Parameter \#1 \$str of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:quoteString\(\) expects string, int\|string\|null given\.$#' identifier: argument.type @@ -2085,6 +2121,12 @@ parameters: count: 1 path: src/Controllers/Database/StructureController.php + - + message: '#^Parameter \#2 \$table of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:getTablesFull\(\) expects array\\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Controllers/Database/StructureController.php + - message: '#^Parameter \#2 \$tableName of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:getTable\(\) expects string, int\|string\|null given\.$#' identifier: argument.type @@ -14100,18 +14142,6 @@ parameters: count: 2 path: src/UserPrivilegesFactory.php - - - message: '#^Binary operation "\." between bool\|float\|int\|string and array\\|string\|false results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/Util.php - - - - message: '#^Binary operation "\." between non\-empty\-string and array\\|string\|false results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/Util.php - - message: '#^Call to deprecated function strftime\(\)\.$#' identifier: function.deprecated @@ -14124,7 +14154,7 @@ parameters: Use dependency injection instead\.$# ''' identifier: staticMethod.deprecated - count: 8 + count: 6 path: src/Util.php - @@ -14133,19 +14163,7 @@ parameters: Use dependency injection instead\.$# ''' identifier: staticMethod.deprecated - count: 9 - path: src/Util.php - - - - message: '#^Cannot access offset ''table_limit_offset'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 4 - path: src/Util.php - - - - message: '#^Cannot access offset ''table_limit_offset_db'' on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 + count: 7 path: src/Util.php - @@ -14166,12 +14184,6 @@ parameters: count: 1 path: src/Util.php - - - message: '#^Method PhpMyAdmin\\Util\:\:getTableListPosition\(\) should return int but returns mixed\.$#' - identifier: return.type - count: 1 - path: src/Util.php - - message: '#^Only booleans are allowed in a negated boolean, int\<0, 2\> given\.$#' identifier: booleanNot.exprNotBoolean @@ -14214,24 +14226,12 @@ parameters: count: 1 path: src/Util.php - - - message: '#^Parameter \#1 \$str of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:escapeMysqlWildcards\(\) expects string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/Util.php - - message: '#^Parameter \#2 \$array of function array_key_exists expects array, mixed given\.$#' identifier: argument.type count: 1 path: src/Util.php - - - message: '#^Parameter \#2 \$table of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:getTablesFull\(\) expects array\\|string, mixed given\.$#' - identifier: argument.type - count: 1 - path: src/Util.php - - message: '#^Variable static method call on PhpMyAdmin\\Util\.$#' identifier: staticMethod.dynamicName @@ -15447,7 +15447,7 @@ parameters: Use dependency injection instead\.$# ''' identifier: staticMethod.deprecated - count: 6 + count: 9 path: tests/unit/Controllers/Database/StructureControllerTest.php - @@ -15456,7 +15456,7 @@ parameters: Use dependency injection instead\.$# ''' identifier: staticMethod.deprecated - count: 5 + count: 7 path: tests/unit/Controllers/Database/StructureControllerTest.php - @@ -18105,7 +18105,7 @@ parameters: Use dependency injection instead\.$# ''' identifier: staticMethod.deprecated - count: 7 + count: 6 path: tests/unit/UtilTest.php - diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 4ced53e30f..b40b318407 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1168,6 +1168,17 @@ + + + + + + + + + + + @@ -1179,11 +1190,17 @@ + + + + + config->settings['NavigationTreeTableSeparator']]]> + config->settings['NavigationTreeTableSeparator']]]> @@ -1208,6 +1225,8 @@ + config->settings['NavigationTreeTableSeparator']]]> + config->settings['NavigationTreeTableSeparator']]]> @@ -1223,6 +1242,10 @@ + + + + @@ -8903,10 +8926,6 @@ - - - - @@ -8920,16 +8939,7 @@ - - - - - - - - - @@ -8938,27 +8948,13 @@ - - - - - settings['NavigationTreeTableSeparator']]]> - settings['NavigationTreeTableSeparator']]]> - - settings['NavigationTreeTableSeparator']]]> - settings['NavigationTreeTableSeparator']]]> - - - - - @@ -9653,6 +9649,11 @@ + + + + + @@ -11424,7 +11425,6 @@ - selectedServer]]> diff --git a/src/Controllers/Database/StructureController.php b/src/Controllers/Database/StructureController.php index 6d18549189..42e5d65e28 100644 --- a/src/Controllers/Database/StructureController.php +++ b/src/Controllers/Database/StructureController.php @@ -43,6 +43,8 @@ use function ceil; use function count; use function implode; use function in_array; +use function is_numeric; +use function is_scalar; use function is_string; use function max; use function mb_substr; @@ -50,6 +52,8 @@ use function md5; use function preg_match; use function preg_quote; use function sprintf; +use function strnatcasecmp; +use function uksort; /** * Handles database structure logic @@ -96,11 +100,11 @@ final class StructureController implements InvocableController */ private function getDatabaseInfo(ServerRequest $request): void { - [$tables, $totalNumTables] = Util::getDbInfo($request, Current::$database); + [$tables, $totalNumTables] = $this->getDbInfo($request, Current::$database); $this->tables = $tables; $this->numTables = count($tables); - $this->position = Util::getTableListPosition($request, Current::$database); + $this->position = $this->getTableListPosition($request, Current::$database); $this->totalNumTables = $totalNumTables; /** @@ -912,4 +916,222 @@ final class StructureController implements InvocableController return null; } } + + /** + * Gets the list of tables in the current db and information about these tables if possible. + * + * @return array{(string|int|null)[][], int} + */ + public function getDbInfo(ServerRequest $request, string $db, bool $isResultLimited = true): array + { + /** + * information about tables in db + */ + $tables = []; + + // Special speedup for newer MySQL Versions (in 4.0 format changed) + if ($this->config->settings['SkipLockedTables'] === true) { + $tables = $this->getTablesWhenOpen($db); + } + + $totalNumTables = null; + if ($tables === []) { + // Set some sorting defaults + $sort = 'Name'; + $sortOrder = 'ASC'; + + /** @var mixed $sortParam */ + $sortParam = $request->getParam('sort'); + if (is_string($sortParam)) { + $sortableNameMappings = [ + 'table' => 'Name', + 'records' => 'Rows', + 'type' => 'Engine', + 'collation' => 'Collation', + 'size' => 'Data_length', + 'overhead' => 'Data_free', + 'creation' => 'Create_time', + 'last_update' => 'Update_time', + 'last_check' => 'Check_time', + 'comment' => 'Comment', + ]; + + // Make sure the sort type is implemented + if (isset($sortableNameMappings[$sortParam])) { + $sort = $sortableNameMappings[$sortParam]; + if ($request->getParam('sort_order') === 'DESC') { + $sortOrder = 'DESC'; + } + } + } + + $groupWithSeparator = false; + $tableType = null; + $limitOffset = 0; + $limitCount = false; + $groupTable = []; + + /** @var mixed $tableGroupParam */ + $tableGroupParam = $request->getParam('tbl_group'); + /** @var mixed $tableTypeParam */ + $tableTypeParam = $request->getParam('tbl_type'); + if ( + is_string($tableGroupParam) && $tableGroupParam !== '' + || is_string($tableTypeParam) && $tableTypeParam !== '' + ) { + if (is_string($tableTypeParam) && $tableTypeParam !== '') { + // only tables for selected type + $tableType = $tableTypeParam; + } + + if (is_string($tableGroupParam) && $tableGroupParam !== '') { + // only tables for selected group + // include the table with the exact name of the group if such exists + $groupTable = $this->dbi->getTablesFull( + $db, + $tableGroupParam, + false, + 0, + false, + $sort, + $sortOrder, + $tableType, + ); + $groupWithSeparator = $tableGroupParam . $this->config->settings['NavigationTreeTableSeparator']; + } + } else { + // all tables in db + // - get the total number of tables + // (needed for proper working of the MaxTableList feature) + $tables = $this->dbi->getTables($db); + $totalNumTables = count($tables); + if ($isResultLimited) { + // fetch the details for a possible limited subset + $limitOffset = $this->getTableListPosition($request, $db); + $limitCount = true; + } + } + + // We must use union operator here instead of array_merge to preserve numerical keys + $tables = $groupTable + $this->dbi->getTablesFull( + $db, + $groupWithSeparator !== false ? $groupWithSeparator : $tables, + $groupWithSeparator !== false, + $limitOffset, + $limitCount, + $sort, + $sortOrder, + $tableType, + ); + } + + return [ + $tables, + $totalNumTables ?? count($tables), // needed for proper working of the MaxTableList feature + ]; + } + + /** + * Gets the list of tables in the current db, taking into account + * that they might be "in use" + * + * @return (string|int|null)[][] list of tables + */ + private function getTablesWhenOpen(string $db): array + { + $openTables = $this->dbi->query( + 'SHOW OPEN TABLES FROM ' . Util::backquote($db) . ' WHERE In_use > 0;', + ); + + // Blending out tables in use + $openTableNames = []; + + /** @var string $tableName */ + foreach ($openTables as ['Table' => $tableName]) { + $openTableNames[] = $tableName; + } + + // is there at least one "in use" table? + if ($openTableNames === []) { + return []; + } + + $tables = []; + $tblGroupSql = ''; + $whereAdded = false; + if ( + isset($_REQUEST['tbl_group']) + && is_scalar($_REQUEST['tbl_group']) + && (string) $_REQUEST['tbl_group'] !== '' + ) { + $group = $this->dbi->escapeMysqlWildcards((string) $_REQUEST['tbl_group']); + $groupWithSeparator = $this->dbi->escapeMysqlWildcards( + $_REQUEST['tbl_group'] . $this->config->settings['NavigationTreeTableSeparator'], + ); + $tblGroupSql .= ' WHERE (' + . Util::backquote('Tables_in_' . $db) + . ' LIKE ' . $this->dbi->quoteString($groupWithSeparator . '%') + . ' OR ' + . Util::backquote('Tables_in_' . $db) + . ' LIKE ' . $this->dbi->quoteString($group) . ')'; + $whereAdded = true; + } + + if (isset($_REQUEST['tbl_type']) && in_array($_REQUEST['tbl_type'], ['table', 'view'], true)) { + $tblGroupSql .= $whereAdded ? ' AND' : ' WHERE'; + if ($_REQUEST['tbl_type'] === 'view') { + $tblGroupSql .= " `Table_type` NOT IN ('BASE TABLE', 'SYSTEM VERSIONED')"; + } else { + $tblGroupSql .= " `Table_type` IN ('BASE TABLE', 'SYSTEM VERSIONED')"; + } + } + + $dbInfoResult = $this->dbi->query('SHOW FULL TABLES FROM ' . Util::backquote($db) . $tblGroupSql); + + if ($dbInfoResult->numRows() > 0) { + $names = []; + while ($tableName = $dbInfoResult->fetchValue()) { + if (! in_array($tableName, $openTableNames, true)) { + $names[] = $tableName; + } else { // table in use + $tables[$tableName] = [ + 'TABLE_NAME' => $tableName, + 'ENGINE' => '', + 'TABLE_TYPE' => '', + 'TABLE_ROWS' => 0, + 'TABLE_COMMENT' => '', + ]; + } + } + + if ($names !== []) { + $tables += $this->dbi->getTablesFull($db, $names); + } + + if ($this->config->settings['NaturalOrder']) { + uksort($tables, strnatcasecmp(...)); + } + } + + return $tables; + } + + public function getTableListPosition(ServerRequest $request, string $db): int + { + if ( + ! isset($_SESSION['tmpval']['table_limit_offset']) + || $_SESSION['tmpval']['table_limit_offset_db'] !== $db + ) { + $_SESSION['tmpval']['table_limit_offset'] = 0; + $_SESSION['tmpval']['table_limit_offset_db'] = $db; + } + + /** @var string|null $posParam */ + $posParam = $request->getParam('pos'); + if (is_numeric($posParam)) { + $_SESSION['tmpval']['table_limit_offset'] = (int) $posParam; + } + + return $_SESSION['tmpval']['table_limit_offset']; + } } diff --git a/src/Util.php b/src/Util.php index 0936b0e904..9e1a045d36 100644 --- a/src/Util.php +++ b/src/Util.php @@ -8,7 +8,6 @@ use DateTimeImmutable; use PhpMyAdmin\ConfigStorage\UserGroupLevel; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Html\Generator; -use PhpMyAdmin\Http\ServerRequest; use PhpMyAdmin\Query\Compatibility; use PhpMyAdmin\SqlParser\Context; use PhpMyAdmin\SqlParser\Token; @@ -42,7 +41,6 @@ use function implode; use function in_array; use function ini_get; use function is_array; -use function is_numeric; use function is_object; use function is_scalar; use function is_string; @@ -77,13 +75,11 @@ use function str_replace; use function str_starts_with; use function strftime; use function strlen; -use function strnatcasecmp; use function strrev; use function strtolower; use function strtr; use function substr; use function trim; -use function uksort; use const ENT_COMPAT; use const ENT_QUOTES; @@ -1483,210 +1479,6 @@ class Util return ''; } - /** - * Gets the list of tables in the current db and information about these tables if possible. - * - * @return array{(string|int|null)[][], int} - */ - public static function getDbInfo(ServerRequest $request, string $db, bool $isResultLimited = true): array - { - /** - * information about tables in db - */ - $tables = []; - - $dbi = DatabaseInterface::getInstance(); - $config = Config::getInstance(); - // Special speedup for newer MySQL Versions (in 4.0 format changed) - if ($config->settings['SkipLockedTables'] === true) { - $tables = self::getTablesWhenOpen($db); - } - - $totalNumTables = null; - if ($tables === []) { - // Set some sorting defaults - $sort = 'Name'; - $sortOrder = 'ASC'; - - /** @var mixed $sortParam */ - $sortParam = $request->getParam('sort'); - if (is_string($sortParam)) { - $sortableNameMappings = [ - 'table' => 'Name', - 'records' => 'Rows', - 'type' => 'Engine', - 'collation' => 'Collation', - 'size' => 'Data_length', - 'overhead' => 'Data_free', - 'creation' => 'Create_time', - 'last_update' => 'Update_time', - 'last_check' => 'Check_time', - 'comment' => 'Comment', - ]; - - // Make sure the sort type is implemented - if (isset($sortableNameMappings[$sortParam])) { - $sort = $sortableNameMappings[$sortParam]; - if ($request->getParam('sort_order') === 'DESC') { - $sortOrder = 'DESC'; - } - } - } - - $groupWithSeparator = false; - $tableType = null; - $limitOffset = 0; - $limitCount = false; - $groupTable = []; - - /** @var mixed $tableGroupParam */ - $tableGroupParam = $request->getParam('tbl_group'); - /** @var mixed $tableTypeParam */ - $tableTypeParam = $request->getParam('tbl_type'); - if ( - is_string($tableGroupParam) && $tableGroupParam !== '' - || is_string($tableTypeParam) && $tableTypeParam !== '' - ) { - if (is_string($tableTypeParam) && $tableTypeParam !== '') { - // only tables for selected type - $tableType = $tableTypeParam; - } - - if (is_string($tableGroupParam) && $tableGroupParam !== '') { - // only tables for selected group - // include the table with the exact name of the group if such exists - $groupTable = $dbi->getTablesFull( - $db, - $tableGroupParam, - false, - 0, - false, - $sort, - $sortOrder, - $tableType, - ); - $groupWithSeparator = $tableGroupParam . $config->settings['NavigationTreeTableSeparator']; - } - } else { - // all tables in db - // - get the total number of tables - // (needed for proper working of the MaxTableList feature) - $tables = $dbi->getTables($db); - $totalNumTables = count($tables); - if ($isResultLimited) { - // fetch the details for a possible limited subset - $limitOffset = self::getTableListPosition($request, $db); - $limitCount = true; - } - } - - // We must use union operator here instead of array_merge to preserve numerical keys - $tables = $groupTable + $dbi->getTablesFull( - $db, - $groupWithSeparator !== false ? $groupWithSeparator : $tables, - $groupWithSeparator !== false, - $limitOffset, - $limitCount, - $sort, - $sortOrder, - $tableType, - ); - } - - return [ - $tables, - $totalNumTables ?? count($tables), // needed for proper working of the MaxTableList feature - ]; - } - - /** - * Gets the list of tables in the current db, taking into account - * that they might be "in use" - * - * @return (string|int|null)[][] list of tables - */ - private static function getTablesWhenOpen(string $db): array - { - $dbi = DatabaseInterface::getInstance(); - - $openTables = $dbi->query( - 'SHOW OPEN TABLES FROM ' . self::backquote($db) . ' WHERE In_use > 0;', - ); - - // Blending out tables in use - $openTableNames = []; - - /** @var string $tableName */ - foreach ($openTables as ['Table' => $tableName]) { - $openTableNames[] = $tableName; - } - - // is there at least one "in use" table? - if ($openTableNames === []) { - return []; - } - - $tables = []; - $tblGroupSql = ''; - $whereAdded = false; - $config = Config::getInstance(); - if ( - isset($_REQUEST['tbl_group']) - && is_scalar($_REQUEST['tbl_group']) - && (string) $_REQUEST['tbl_group'] !== '' - ) { - $group = $dbi->escapeMysqlWildcards((string) $_REQUEST['tbl_group']); - $groupWithSeparator = $dbi->escapeMysqlWildcards( - $_REQUEST['tbl_group'] . $config->settings['NavigationTreeTableSeparator'], - ); - $tblGroupSql .= ' WHERE (' - . self::backquote('Tables_in_' . $db) - . ' LIKE ' . $dbi->quoteString($groupWithSeparator . '%') - . ' OR ' - . self::backquote('Tables_in_' . $db) - . ' LIKE ' . $dbi->quoteString($group) . ')'; - $whereAdded = true; - } - - if (isset($_REQUEST['tbl_type']) && in_array($_REQUEST['tbl_type'], ['table', 'view'], true)) { - $tblGroupSql .= $whereAdded ? ' AND' : ' WHERE'; - if ($_REQUEST['tbl_type'] === 'view') { - $tblGroupSql .= " `Table_type` NOT IN ('BASE TABLE', 'SYSTEM VERSIONED')"; - } else { - $tblGroupSql .= " `Table_type` IN ('BASE TABLE', 'SYSTEM VERSIONED')"; - } - } - - $dbInfoResult = $dbi->query('SHOW FULL TABLES FROM ' . self::backquote($db) . $tblGroupSql); - - if ($dbInfoResult->numRows() > 0) { - $names = []; - while ($tableName = $dbInfoResult->fetchValue()) { - if (! in_array($tableName, $openTableNames, true)) { - $names[] = $tableName; - } else { // table in use - $tables[$tableName] = [ - 'TABLE_NAME' => $tableName, - 'ENGINE' => '', - 'TABLE_TYPE' => '', - 'TABLE_ROWS' => 0, - 'TABLE_COMMENT' => '', - ]; - } - } - - if ($names !== []) { - $tables += $dbi->getTablesFull($db, $names); - } - - if ($config->settings['NaturalOrder']) { - uksort($tables, strnatcasecmp(...)); - } - } - - return $tables; - } - /** * Returns list of used PHP extensions. * @@ -1929,25 +1721,6 @@ class Util return ''; } - public static function getTableListPosition(ServerRequest $request, string $db): int - { - if ( - ! isset($_SESSION['tmpval']['table_limit_offset']) - || $_SESSION['tmpval']['table_limit_offset_db'] !== $db - ) { - $_SESSION['tmpval']['table_limit_offset'] = 0; - $_SESSION['tmpval']['table_limit_offset_db'] = $db; - } - - /** @var string|null $posParam */ - $posParam = $request->getParam('pos'); - if (is_numeric($posParam)) { - $_SESSION['tmpval']['table_limit_offset'] = (int) $posParam; - } - - return $_SESSION['tmpval']['table_limit_offset']; - } - /** * Maximum upload size as limited by PHP * Used with permission from Moodle (https://moodle.org/) by Martin Dougiamas diff --git a/tests/unit/Controllers/Database/StructureControllerTest.php b/tests/unit/Controllers/Database/StructureControllerTest.php index 0979001d80..ae9446c819 100644 --- a/tests/unit/Controllers/Database/StructureControllerTest.php +++ b/tests/unit/Controllers/Database/StructureControllerTest.php @@ -19,6 +19,7 @@ use PhpMyAdmin\Tests\AbstractTestCase; use PhpMyAdmin\Tests\Stubs\ResponseRenderer as ResponseStub; use PhpMyAdmin\Tracking\TrackingChecker; use PHPUnit\Framework\Attributes\CoversClass; +use Psr\Http\Message\ServerRequestInterface; use ReflectionClass; use ReflectionException; @@ -379,4 +380,105 @@ class StructureControllerTest extends AbstractTestCase ), ); } + + public function testGetDbInfo(): void + { + Config::getInstance()->selectedServer['DisableIS'] = true; + + $dbiDummy = $this->createDbiDummy(); + $dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['test_table']], ['Tables_in_test_db']); + DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy); + + $dbi = DatabaseInterface::getInstance(); + $structureController = new StructureController( + $this->response, + $this->template, + $this->relation, + $this->replication, + $dbi, + self::createStub(TrackingChecker::class), + self::createStub(PageSettings::class), + new DbTableExists($dbi), + Config::getInstance(), + ); + + $tableInfo = [ + 'Name' => 'test_table', + 'Engine' => 'InnoDB', + 'Version' => '10', + 'Row_format' => 'Dynamic', + 'Rows' => '3', + 'Avg_row_length' => '5461', + 'Data_length' => '16384', + 'Max_data_length' => '0', + 'Index_length' => '0', + 'Data_free' => '0', + 'Auto_increment' => '4', + 'Create_time' => '2011-12-13 14:15:16', + 'Update_time' => null, + 'Check_time' => null, + 'Collation' => 'utf8mb4_general_ci', + 'Checksum' => null, + 'Create_options' => '', + 'Comment' => '', + 'Max_index_length' => '0', + 'Temporary' => 'N', + 'Type' => 'InnoDB', + 'TABLE_SCHEMA' => 'test_db', + 'TABLE_NAME' => 'test_table', + 'ENGINE' => 'InnoDB', + 'VERSION' => '10', + 'ROW_FORMAT' => 'Dynamic', + 'TABLE_ROWS' => '3', + 'AVG_ROW_LENGTH' => '5461', + 'DATA_LENGTH' => '16384', + 'MAX_DATA_LENGTH' => '0', + 'INDEX_LENGTH' => '0', + 'DATA_FREE' => '0', + 'AUTO_INCREMENT' => '4', + 'CREATE_TIME' => '2011-12-13 14:15:16', + 'UPDATE_TIME' => null, + 'CHECK_TIME' => null, + 'TABLE_COLLATION' => 'utf8mb4_general_ci', + 'CHECKSUM' => null, + 'CREATE_OPTIONS' => '', + 'TABLE_COMMENT' => '', + 'TABLE_TYPE' => 'BASE TABLE', + ]; + $expected = [['test_table' => $tableInfo], 1]; + $actual = $structureController->getDbInfo(self::createStub(ServerRequest::class), 'test_db'); + self::assertSame($expected, $actual); + } + + public function testGetTableListPosition(): void + { + $dbi = DatabaseInterface::getInstance(); + $structureController = new StructureController( + $this->response, + $this->template, + $this->relation, + $this->replication, + $dbi, + self::createStub(TrackingChecker::class), + self::createStub(PageSettings::class), + new DbTableExists($dbi), + Config::getInstance(), + ); + + // Default 0 + $actual = $structureController->getTableListPosition(self::createStub(ServerRequest::class), 'test_db'); + self::assertSame(0, $actual); + + // From POST + $requestStub = self::createStub(ServerRequestInterface::class); + $requestStub->method('getQueryParams')->willReturn([]); + $requestStub->method('getParsedBody')->willReturn(['pos' => '250']); + $request = new ServerRequest($requestStub); + $actual = $structureController->getTableListPosition($request, 'test_db'); + self::assertSame(250, $actual); + + // From SESSION + $actual = $structureController->getTableListPosition(self::createStub(ServerRequest::class), 'test_db'); + self::assertSame(250, $actual); + } } diff --git a/tests/unit/UtilTest.php b/tests/unit/UtilTest.php index b5ea3915d1..78a2c614a3 100644 --- a/tests/unit/UtilTest.php +++ b/tests/unit/UtilTest.php @@ -8,7 +8,6 @@ use DateTimeImmutable; use PhpMyAdmin\Config; use PhpMyAdmin\Current; use PhpMyAdmin\Dbal\DatabaseInterface; -use PhpMyAdmin\Http\ServerRequest; use PhpMyAdmin\MoTranslator\Loader; use PhpMyAdmin\SqlParser\Context; use PhpMyAdmin\SqlParser\Token; @@ -18,7 +17,6 @@ use PhpMyAdmin\Version; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\RequiresPhpExtension; -use Psr\Http\Message\ServerRequestInterface; use function __; use function _setlocale; @@ -1325,81 +1323,6 @@ SQL; self::assertSame(['route=/table/structure', 'db=sakila', 'table=address'], $actual); } - public function testGetDbInfo(): void - { - Config::getInstance()->selectedServer['DisableIS'] = true; - - $dbiDummy = $this->createDbiDummy(); - $dbiDummy->addResult('SHOW TABLES FROM `test_db`;', [['test_table']], ['Tables_in_test_db']); - DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy); - - $tableInfo = [ - 'Name' => 'test_table', - 'Engine' => 'InnoDB', - 'Version' => '10', - 'Row_format' => 'Dynamic', - 'Rows' => '3', - 'Avg_row_length' => '5461', - 'Data_length' => '16384', - 'Max_data_length' => '0', - 'Index_length' => '0', - 'Data_free' => '0', - 'Auto_increment' => '4', - 'Create_time' => '2011-12-13 14:15:16', - 'Update_time' => null, - 'Check_time' => null, - 'Collation' => 'utf8mb4_general_ci', - 'Checksum' => null, - 'Create_options' => '', - 'Comment' => '', - 'Max_index_length' => '0', - 'Temporary' => 'N', - 'Type' => 'InnoDB', - 'TABLE_SCHEMA' => 'test_db', - 'TABLE_NAME' => 'test_table', - 'ENGINE' => 'InnoDB', - 'VERSION' => '10', - 'ROW_FORMAT' => 'Dynamic', - 'TABLE_ROWS' => '3', - 'AVG_ROW_LENGTH' => '5461', - 'DATA_LENGTH' => '16384', - 'MAX_DATA_LENGTH' => '0', - 'INDEX_LENGTH' => '0', - 'DATA_FREE' => '0', - 'AUTO_INCREMENT' => '4', - 'CREATE_TIME' => '2011-12-13 14:15:16', - 'UPDATE_TIME' => null, - 'CHECK_TIME' => null, - 'TABLE_COLLATION' => 'utf8mb4_general_ci', - 'CHECKSUM' => null, - 'CREATE_OPTIONS' => '', - 'TABLE_COMMENT' => '', - 'TABLE_TYPE' => 'BASE TABLE', - ]; - $expected = [['test_table' => $tableInfo], 1]; - $actual = Util::getDbInfo(self::createStub(ServerRequest::class), 'test_db'); - self::assertSame($expected, $actual); - } - - public function testGetTableListPosition(): void - { - // Default 0 - $actual = Util::getTableListPosition(self::createStub(ServerRequest::class), 'test_db'); - self::assertSame(0, $actual); - - // From POST - $requestStub = self::createStub(ServerRequestInterface::class); - $requestStub->method('getQueryParams')->willReturn([]); - $requestStub->method('getParsedBody')->willReturn(['pos' => '250']); - $request = new ServerRequest($requestStub); - $actual = Util::getTableListPosition($request, 'test_db'); - self::assertSame(250, $actual); - - // From SESSION - $actual = Util::getTableListPosition(self::createStub(ServerRequest::class), 'test_db'); - self::assertSame(250, $actual); - } - /** * Tests for Util::testIsUUIDSupported() method. *