Merge #20025 - Nodes refactoring

Pull-request: #20025
Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
William Desportes 2026-01-17 13:36:15 +01:00
commit cd1b3ad028
No known key found for this signature in database
GPG Key ID: 70684F4717D49A31
8 changed files with 298 additions and 264 deletions

View File

@ -7765,85 +7765,7 @@ parameters:
path: src/Navigation/Nodes/Node.php
-
message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
identifier: empty.notAllowed
count: 4
path: src/Navigation/Nodes/Node.php
-
message: '#^Method PhpMyAdmin\\Navigation\\Nodes\\Node\:\:getDatabasesToSearch\(\) should return array\<mixed\> but returns list\<string\>\|string\.$#'
identifier: return.type
count: 1
path: src/Navigation/Nodes/Node.php
-
message: '#^Parameter \#1 \$array of function sort expects TArray of array\<string\>, array\<string\>\|string given\.$#'
identifier: argument.type
count: 1
path: src/Navigation/Nodes/Node.php
-
message: '#^Parameter \#1 \$db of method PhpMyAdmin\\Navigation\\Nodes\\Node\:\:isHideDb\(\) expects string, string\|null given\.$#'
identifier: argument.type
count: 4
path: src/Navigation/Nodes/Node.php
-
message: '#^Parameter \#1 \$haystack of function strstr expects string, string\|null given\.$#'
identifier: argument.type
count: 4
path: src/Navigation/Nodes/Node.php
-
message: '#^Parameter \#1 \$str of method PhpMyAdmin\\Dbal\\DatabaseInterface\:\:quoteString\(\) expects string, mixed given\.$#'
identifier: argument.type
count: 5
path: src/Navigation/Nodes/Node.php
-
message: '#^Binary operation "\." between mixed and ''\)'' results in an error\.$#'
identifier: binaryOp.invalid
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Binary operation "\.\=" between mixed and '' \('' results in an error\.$#'
identifier: assignOp.invalid
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Binary operation "\.\=" between mixed and '', '' results in an error\.$#'
identifier: assignOp.invalid
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#'
identifier: empty.notAllowed
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Parameter \#1 \$item of method PhpMyAdmin\\Navigation\\Nodes\\NodeColumn\:\:getDisplayName\(\) expects array\<string, mixed\>, array\<mixed\> given\.$#'
identifier: argument.type
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Parameter \#1 \$key of method PhpMyAdmin\\Navigation\\Nodes\\NodeColumn\:\:getColumnIcon\(\) expects string, mixed given\.$#'
identifier: argument.type
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Parameter \#2 \$name of method PhpMyAdmin\\Navigation\\Nodes\\Node\:\:__construct\(\) expects string, mixed given\.$#'
identifier: argument.type
count: 1
path: src/Navigation/Nodes/NodeColumn.php
-
message: '#^Parameter \#2 \$value of method PhpMyAdmin\\Navigation\\Nodes\\NodeColumn\:\:getTruncateValue\(\) expects string, mixed given\.$#'
message: '#^Parameter \#2 \$name of method PhpMyAdmin\\Navigation\\Nodes\\Node\:\:__construct\(\) expects string, string\|null given\.$#'
identifier: argument.type
count: 1
path: src/Navigation/Nodes/NodeColumn.php
@ -7893,6 +7815,12 @@ parameters:
count: 1
path: src/Navigation/Nodes/NodeTable.php
-
message: '#^Parameter \#2 \$item of class PhpMyAdmin\\Navigation\\Nodes\\NodeColumn constructor expects array\{name\: string\|null, key\: string, type\: string\|null, default\: string\|null, nullable\: string\}, array\<string\|null\> given\.$#'
identifier: argument.type
count: 1
path: src/Navigation/Nodes/NodeTable.php
-
message: '#^Method PhpMyAdmin\\Navigation\\Nodes\\ObjectFetcher\:\:getEventsFromDb\(\) should return array\<string\> but returns list\<string\|null\>\.$#'
identifier: return.type
@ -16335,7 +16263,7 @@ parameters:
Use dependency injection instead\.$#
'''
identifier: staticMethod.deprecated
count: 2
count: 7
path: tests/unit/Navigation/Nodes/NodeTableTest.php
-
@ -16344,13 +16272,7 @@ parameters:
Use dependency injection instead\.$#
'''
identifier: staticMethod.deprecated
count: 7
path: tests/unit/Navigation/Nodes/NodeTest.php
-
message: '#^Property PhpMyAdmin\\Config\:\:\$selectedServer \(array\{host\: string, port\: string, socket\: string, ssl\: bool, ssl_key\: string\|null, ssl_cert\: string\|null, ssl_ca\: string\|null, ssl_ca_path\: string\|null, \.\.\.\}\) does not accept array\{host\: string, port\: string, socket\: string, ssl\: bool, ssl_key\: string\|null, ssl_cert\: string\|null, ssl_ca\: string\|null, ssl_ca_path\: string\|null, \.\.\.\}\.$#'
identifier: assign.propertyType
count: 3
count: 8
path: tests/unit/Navigation/Nodes/NodeTest.php
-

View File

@ -5276,67 +5276,16 @@
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
</DeprecatedMethod>
<MixedArgument>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
</MixedArgument>
<MixedAssignment>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
<code><![CDATA[$db]]></code>
</MixedAssignment>
<PossiblyInvalidArgument>
<code><![CDATA[$databases]]></code>
</PossiblyInvalidArgument>
<PossiblyNullArgument>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
<code><![CDATA[$arr[0]]]></code>
</PossiblyNullArgument>
<PossiblyNullArrayOffset>
<code><![CDATA[$prefixMap]]></code>
<code><![CDATA[$prefixMap]]></code>
<code><![CDATA[$prefixMap]]></code>
<code><![CDATA[$prefixMap]]></code>
</PossiblyNullArrayOffset>
<PossiblyNullOperand>
<code><![CDATA[$arr[0]]]></code>
</PossiblyNullOperand>
<RiskyTruthyFalsyComparison>
<code><![CDATA[empty($this->config->selectedServer['hide_db'])]]></code>
<code><![CDATA[empty($this->config->selectedServer['hide_db'])]]></code>
<code><![CDATA[empty($this->config->selectedServer['only_db'])]]></code>
<code><![CDATA[empty($this->config->selectedServer['only_db'])]]></code>
</RiskyTruthyFalsyComparison>
</file>
<file src="src/Navigation/Nodes/NodeColumn.php">
<MixedArgument>
<code><![CDATA[$item['key']]]></code>
<PossiblyNullArgument>
<code><![CDATA[$item['name']]]></code>
<code><![CDATA[$value]]></code>
</MixedArgument>
<MixedArgumentTypeCoercion>
<code><![CDATA[$item]]></code>
</MixedArgumentTypeCoercion>
<MixedAssignment>
<code><![CDATA[$retval]]></code>
<code><![CDATA[$value]]></code>
</MixedAssignment>
<MixedOperand>
</PossiblyNullArgument>
<PossiblyNullOperand>
<code><![CDATA[$retval]]></code>
<code><![CDATA[$retval]]></code>
<code><![CDATA[$retval]]></code>
</MixedOperand>
</PossiblyNullOperand>
</file>
<file src="src/Navigation/Nodes/NodeDatabase.php">
<DeprecatedMethod>
@ -5358,6 +5307,9 @@
<DeprecatedMethod>
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
</DeprecatedMethod>
<MixedArgumentTypeCoercion>
<code><![CDATA[$row]]></code>
</MixedArgumentTypeCoercion>
<PossiblyInvalidPropertyFetch>
<code><![CDATA[$this->getRealParent()->realName]]></code>
<code><![CDATA[$this->getRealParent()->realName]]></code>
@ -10503,6 +10455,11 @@
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
</DeprecatedMethod>
</file>
<file src="tests/unit/Navigation/Nodes/NodeTest.php">
@ -10514,11 +10471,8 @@
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
<code><![CDATA[Config::getInstance()]]></code>
</DeprecatedMethod>
<InvalidPropertyAssignmentValue>
<code><![CDATA[$config->selectedServer]]></code>
<code><![CDATA[$config->selectedServer]]></code>
</InvalidPropertyAssignmentValue>
</file>
<file src="tests/unit/Navigation/Nodes/ObjectFetcherTest.php">
<DeprecatedMethod>

View File

@ -225,8 +225,8 @@ class NavigationTree
return (int) $this->dbi->fetchValue(
sprintf(
$query,
$this->config->settings['FirstLevelNavigationItems'],
$this->config->settings['FirstLevelNavigationItems'],
$this->config->config->FirstLevelNavigationItems,
$this->config->config->FirstLevelNavigationItems,
$this->dbi->quoteString($this->config->settings['NavigationTreeDbSeparator']),
$this->dbi->quoteString(Current::$database),
),
@ -277,7 +277,7 @@ class NavigationTree
}
}
$navItems = $this->config->settings['FirstLevelNavigationItems'];
$navItems = $this->config->config->FirstLevelNavigationItems;
return (int) floor(count($prefixMap) / $navItems) * $navItems;
}
@ -1129,7 +1129,7 @@ class NavigationTree
['server' => Current::$server],
Url::getFromRoute('/navigation'),
'frame_navigation',
$this->config->settings['FirstLevelNavigationItems'],
$this->config->config->FirstLevelNavigationItems,
'pos',
['dbselector'],
);
@ -1278,7 +1278,7 @@ class NavigationTree
['server' => Current::$server],
Url::getFromRoute('/navigation'),
'frame_navigation',
$this->config->settings['FirstLevelNavigationItems'],
$this->config->config->FirstLevelNavigationItems,
'pos',
['dbselector'],
);

View File

@ -318,7 +318,7 @@ class Node
int $pos,
string $searchClause = '',
): array {
if (isset($this->config->selectedServer['DisableIS']) && ! $this->config->selectedServer['DisableIS']) {
if (! $this->config->selectedServer['DisableIS']) {
return $this->getDataFromInfoSchema($pos, $searchClause);
}
@ -345,7 +345,7 @@ class Node
! $this->config->settings['NavigationTreeEnableGrouping']
|| ! $this->config->settings['ShowDatabasesNavigationAsTree']
) {
if (isset($this->config->selectedServer['DisableIS']) && ! $this->config->selectedServer['DisableIS']) {
if (! $this->config->selectedServer['DisableIS']) {
$query = 'SELECT COUNT(*) ';
$query .= 'FROM INFORMATION_SCHEMA.SCHEMATA ';
$query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
@ -392,14 +392,15 @@ class Node
continue;
}
while ($arr = $handle->fetchRow()) {
if ($this->isHideDb($arr[0])) {
/** @var string $value */
foreach ($handle->fetchAllColumn() as $value) {
if ($this->isHideDb($value)) {
continue;
}
$prefix = strstr($arr[0], $dbSeparator, true);
$prefix = strstr($value, $dbSeparator, true);
if ($prefix === false) {
$prefix = $arr[0];
$prefix = $value;
}
$prefixMap[$prefix] = 1;
@ -414,10 +415,11 @@ class Node
$query .= $this->getWhereClause('Database', $searchClause);
$handle = $dbi->tryQuery($query);
if ($handle !== false) {
while ($arr = $handle->fetchRow()) {
$prefix = strstr($arr[0], $dbSeparator, true);
/** @var string $value */
foreach ($handle->fetchAllColumn() as $value) {
$prefix = strstr($value, $dbSeparator, true);
if ($prefix === false) {
$prefix = $arr[0];
$prefix = $value;
}
$prefixMap[$prefix] = 1;
@ -434,7 +436,7 @@ class Node
*/
private function isHideDb(string $db): bool
{
return ! empty($this->config->selectedServer['hide_db'])
return $this->config->selectedServer['hide_db'] !== ''
&& preg_match('/' . $this->config->selectedServer['hide_db'] . '/', $db) === 1;
}
@ -446,15 +448,15 @@ class Node
*
* @param string $searchClause search clause
*
* @return mixed[] array of databases
* @return string[] array of databases
*/
private function getDatabasesToSearch(UserPrivileges $userPrivileges, string $searchClause): array
{
$databases = [];
if ($searchClause !== '') {
$databases = ['%' . DatabaseInterface::getInstance()->escapeMysqlWildcards($searchClause) . '%'];
} elseif (! empty($this->config->selectedServer['only_db'])) {
$databases = $this->config->selectedServer['only_db'];
} elseif ($this->config->selectedServer['only_db'] !== '') {
$databases = (array) $this->config->selectedServer['only_db'];
} elseif ($userPrivileges->databasesToTest !== false && $userPrivileges->databasesToTest !== []) {
$databases = $userPrivileges->databasesToTest;
}
@ -480,12 +482,12 @@ class Node
. ' LIKE ' . $dbi->quoteString('%' . $dbi->escapeMysqlWildcards($searchClause) . '%') . ' ';
}
if (! empty($this->config->selectedServer['hide_db'])) {
if ($this->config->selectedServer['hide_db'] !== '') {
$whereClause .= 'AND ' . Util::backquote($columnName)
. ' NOT REGEXP ' . $dbi->quoteString($this->config->selectedServer['hide_db']) . ' ';
}
if (! empty($this->config->selectedServer['only_db'])) {
if ($this->config->selectedServer['only_db'] !== '') {
if (is_string($this->config->selectedServer['only_db'])) {
$this->config->selectedServer['only_db'] = [$this->config->selectedServer['only_db']];
}
@ -591,7 +593,7 @@ class Node
*/
private function getDataFromInfoSchema(int $pos, string $searchClause): array
{
$maxItems = $this->config->settings['FirstLevelNavigationItems'];
$maxItems = $this->config->config->FirstLevelNavigationItems;
$dbi = DatabaseInterface::getInstance();
if (
! $this->config->settings['NavigationTreeEnableGrouping']
@ -632,7 +634,7 @@ class Node
*/
private function getDataFromShowDatabases(int $pos, string $searchClause): array
{
$maxItems = $this->config->settings['FirstLevelNavigationItems'];
$maxItems = $this->config->config->FirstLevelNavigationItems;
$dbi = DatabaseInterface::getInstance();
if (
! $this->config->settings['NavigationTreeEnableGrouping']
@ -646,22 +648,7 @@ class Node
return [];
}
$count = 0;
if (! $handle->seek($pos)) {
return [];
}
$retval = [];
while ($arr = $handle->fetchRow()) {
if ($count >= $maxItems) {
break;
}
$retval[] = $arr[0];
$count++;
}
return $retval;
return array_slice($handle->fetchAllColumn(), $pos, $maxItems);
}
$dbSeparator = $this->config->settings['NavigationTreeDbSeparator'];
@ -673,10 +660,10 @@ class Node
if ($handle !== false) {
$prefixMap = [];
$total = $pos + $maxItems;
while ($arr = $handle->fetchRow()) {
$prefix = strstr($arr[0], $dbSeparator, true);
while ($value = $handle->fetchValue()) {
$prefix = strstr($value, $dbSeparator, true);
if ($prefix === false) {
$prefix = $arr[0];
$prefix = $value;
}
$prefixMap[$prefix] = 1;
@ -710,11 +697,11 @@ class Node
* @param int $pos The offset of the list within the results.
* @param string $searchClause A string used to filter the results of the query.
*
* @return list<string|null>
* @return list<string>
*/
private function getDataFromShowDatabasesLike(UserPrivileges $userPrivileges, int $pos, string $searchClause): array
{
$maxItems = $this->config->settings['FirstLevelNavigationItems'];
$maxItems = $this->config->config->FirstLevelNavigationItems;
$dbi = DatabaseInterface::getInstance();
if (
! $this->config->settings['NavigationTreeEnableGrouping']
@ -728,17 +715,18 @@ class Node
continue;
}
while ($arr = $handle->fetchRow()) {
if ($this->isHideDb($arr[0])) {
/** @var string $value */
foreach ($handle->fetchAllColumn() as $value) {
if ($this->isHideDb($value)) {
continue;
}
if (in_array($arr[0], $retval, true)) {
if (in_array($value, $retval, true)) {
continue;
}
if ($pos <= 0 && $count < $maxItems) {
$retval[] = $arr[0];
$retval[] = $value;
$count++;
}
@ -761,14 +749,15 @@ class Node
continue;
}
while ($arr = $handle->fetchRow()) {
if ($this->isHideDb($arr[0])) {
/** @var string $value */
foreach ($handle->fetchAllColumn() as $value) {
if ($this->isHideDb($value)) {
continue;
}
$prefix = strstr($arr[0], $dbSeparator, true);
$prefix = strstr($value, $dbSeparator, true);
if ($prefix === false) {
$prefix = $arr[0];
$prefix = $value;
}
$prefixMap[$prefix] = 1;
@ -786,19 +775,20 @@ class Node
continue;
}
while ($arr = $handle->fetchRow()) {
if ($this->isHideDb($arr[0])) {
/** @var string $value */
foreach ($handle->fetchAllColumn() as $value) {
if ($this->isHideDb($value)) {
continue;
}
if (in_array($arr[0], $retval, true)) {
if (in_array($value, $retval, true)) {
continue;
}
foreach ($prefixes as $prefix) {
$startsWith = str_starts_with($arr[0] . $dbSeparator, $prefix . $dbSeparator);
$startsWith = str_starts_with($value . $dbSeparator, $prefix . $dbSeparator);
if ($startsWith) {
$retval[] = $arr[0];
$retval[] = $value;
break;
}
}

View File

@ -20,7 +20,7 @@ class NodeColumn extends Node
{
public string $displayName;
/** @param mixed[] $item array to identify the column node */
/** @param array{name:string|null, key:string, type:string|null, default:string|null, nullable:string} $item */
public function __construct(Config $config, array $item)
{
$this->displayName = $this->getDisplayName($item);
@ -60,7 +60,7 @@ class NodeColumn extends Node
/**
* Get displayable name for navigation tree (key_type, data_type, default)
*
* @param array<string, mixed> $item Item is array containing required info
* @param array{name:string|null, key:string, type:string|null, default:string|null, nullable:string} $item
*
* @return string Display name for navigation tree
*/
@ -69,7 +69,7 @@ class NodeColumn extends Node
$retval = $item['name'];
$isFirst = true;
foreach ($item as $key => $value) {
if (empty($value) || $key === 'name') {
if ($value === null || $value === '' || $key === 'name') {
continue;
}

View File

@ -13,7 +13,7 @@ use PhpMyAdmin\UserPrivileges;
use PhpMyAdmin\Util;
use function __;
use function in_array;
use function array_slice;
/**
* Represents a columns node in the navigation tree
@ -127,7 +127,7 @@ class NodeTable extends NodeDatabaseChild
$query .= 'FROM `INFORMATION_SCHEMA`.`COLUMNS` ';
$query .= 'WHERE `TABLE_NAME`=' . $dbi->quoteString($table) . ' ';
$query .= 'AND `TABLE_SCHEMA`=' . $dbi->quoteString($db) . ' ';
$query .= 'ORDER BY `COLUMN_NAME` ASC ';
$query .= 'ORDER BY `ORDINAL_POSITION` ASC ';
$query .= 'LIMIT ' . $pos . ', ' . $maxItems;
$columnNodes = [];
@ -144,26 +144,18 @@ class NodeTable extends NodeDatabaseChild
return [];
}
$retval = [];
$count = 0;
if ($handle->seek($pos)) {
foreach ($handle as $arr) {
if ($count >= $maxItems) {
return $retval;
}
$retval[] = new NodeColumn($this->config, [
'name' => $arr['Field'],
'key' => $arr['Key'],
'type' => Util::extractColumnSpec($arr['Type'])['type'],
'default' => $arr['Default'],
'nullable' => $arr['Null'] === 'NO' ? '' : 'nullable',
]);
$count++;
}
$columnNodes = [];
foreach (array_slice($handle->fetchAllAssoc(), $pos, $maxItems) as $arr) {
$columnNodes[] = new NodeColumn($this->config, [
'name' => $arr['Field'],
'key' => $arr['Key'] ?? '',
'type' => Util::extractColumnSpec($arr['Type'])['type'],
'default' => $arr['Default'],
'nullable' => $arr['Null'] === 'NO' ? '' : 'nullable',
]);
}
return $retval;
return $columnNodes;
}
/** @return NodeIndex[] */
@ -178,24 +170,9 @@ class NodeTable extends NodeDatabaseChild
return [];
}
$indexNames = [];
$count = 0;
/** @var string $keyName */
foreach ($handle as ['Key_name' => $keyName]) {
if (in_array($keyName, $indexNames, true)) {
continue;
}
if ($pos <= 0 && $count < $maxItems) {
$indexNames[] = $keyName;
$count++;
}
$pos--;
}
$indexNodes = [];
foreach ($indexNames as $indexName) {
/** @var string $indexName */
foreach (array_slice($handle->fetchAllAssoc(), $pos, $maxItems) as ['Key_name' => $indexName]) {
$indexNodes[] = new NodeIndex($this->config, $indexName);
}
@ -232,21 +209,13 @@ class NodeTable extends NodeDatabaseChild
return [];
}
$retval = [];
$count = 0;
if ($handle->seek($pos)) {
/** @var string $triggerName */
foreach ($handle as ['Trigger' => $triggerName]) {
if ($count >= $maxItems) {
return $retval;
}
$retval[] = new NodeTrigger($this->config, $triggerName);
$count++;
}
$triggerNodes = [];
/** @var string $triggerName */
foreach (array_slice($handle->fetchAllAssoc(), $pos, $maxItems) as ['Trigger' => $triggerName]) {
$triggerNodes[] = new NodeTrigger($this->config, $triggerName);
}
return $retval;
return $triggerNodes;
}
/**

View File

@ -5,8 +5,11 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Navigation\Nodes;
use PhpMyAdmin\Config;
use PhpMyAdmin\Dbal\DatabaseInterface;
use PhpMyAdmin\Navigation\Nodes\NodeDatabase;
use PhpMyAdmin\Navigation\Nodes\NodeTable;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\DummyResult;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
@ -64,4 +67,166 @@ class NodeTableTest extends AbstractTestCase
['/sql', 'b_browse', 'Browse'],
];
}
public function testGetColumnsWithoutDisableIS(): void
{
$config = Config::getInstance();
$dbi = self::createMock(DatabaseInterface::class);
$dbi->expects(self::once())
->method('fetchResultSimple')
->willReturnOnConsecutiveCalls(
[
['name' => 'foo1', 'type' => 'varchar(255)', 'key' => '', 'default' => '', 'nullable' => ''],
['name' => 'foo2', 'type' => 'varchar(64)', 'key' => '', 'default' => '', 'nullable' => ''],
['name' => 'foo3', 'type' => 'int(11)', 'key' => '', 'default' => '0', 'nullable' => ''],
['name' => 'foo4', 'type' => 'text', 'key' => '', 'default' => '', 'nullable' => ''],
],
);
$nodeContainer = new NodeDatabase($config, 'DB');
$nodeTable = new NodeTable($config, 'node');
$nodeContainer->addChild($nodeTable);
$result = $nodeTable->getColumns($dbi, 1);
self::assertCount(4, $result);
self::assertSame('foo1 (varchar(255))', $result[0]->displayName);
self::assertSame('foo2 (varchar(64))', $result[1]->displayName);
self::assertSame('foo3 (int(11), 0)', $result[2]->displayName);
self::assertSame('foo4 (text)', $result[3]->displayName);
}
public function testGetColumnsWithDisableIS(): void
{
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = true;
$config->set('MaxNavigationItems', 2);
$dbi = self::createMock(DatabaseInterface::class);
$resultStub = self::createMock(DummyResult::class);
$dbi->expects(self::once())
->method('tryQuery')
->with('SHOW COLUMNS FROM `node` FROM `DB`')
->willReturn($resultStub);
$resultStub->expects(self::once())
->method('fetchAllAssoc')
->willReturnOnConsecutiveCalls(
[
['Field' => 'foo1', 'Type' => 'varchar(255)', 'Key' => '', 'Default' => '', 'Null' => 'NO'],
['Field' => 'foo2', 'Type' => 'varchar(64)', 'Key' => '', 'Default' => '', 'Null' => 'NO'],
['Field' => 'foo3', 'Type' => 'int(11)', 'Key' => '', 'Default' => '0', 'Null' => 'NO'],
['Field' => 'foo4', 'Type' => 'text', 'Key' => '', 'Default' => '', 'Null' => 'NO'],
],
);
$nodeContainer = new NodeDatabase($config, 'DB');
$nodeTable = new NodeTable($config, 'node');
$nodeContainer->addChild($nodeTable);
$result = $nodeTable->getColumns($dbi, 1);
self::assertCount(2, $result);
self::assertSame('foo2 (varchar)', $result[0]->displayName);
self::assertSame('foo3 (int, 0)', $result[1]->displayName);
}
public function testGetTriggersWithoutDisableIS(): void
{
$config = Config::getInstance();
$dbi = self::createMock(DatabaseInterface::class);
$dbi->expects(self::once())
->method('fetchSingleColumn')
->willReturnOnConsecutiveCalls(
[
'foo1',
'foo2',
'foo3',
'foo4',
],
);
$nodeContainer = new NodeDatabase($config, 'DB');
$nodeTable = new NodeTable($config, 'node');
$nodeContainer->addChild($nodeTable);
$result = $nodeTable->getTriggers($dbi, 1);
self::assertCount(4, $result);
self::assertSame('foo1', $result[0]->name);
self::assertSame('foo2', $result[1]->name);
self::assertSame('foo3', $result[2]->name);
self::assertSame('foo4', $result[3]->name);
}
public function testGetTriggersWithDisableIS(): void
{
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = true;
$config->set('MaxNavigationItems', 2);
$dbi = self::createMock(DatabaseInterface::class);
$resultStub = self::createMock(DummyResult::class);
$dbi->expects(self::once())->method('quoteString')
->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
$dbi->expects(self::once())
->method('tryQuery')
->with('SHOW TRIGGERS FROM `DB` WHERE `Table` = \'node\'')
->willReturn($resultStub);
$resultStub->expects(self::once())
->method('fetchAllAssoc')
->willReturnOnConsecutiveCalls(
[
['Trigger' => 'foo1'],
['Trigger' => 'foo2'],
['Trigger' => 'foo3'],
['Trigger' => 'foo4'],
],
);
$nodeContainer = new NodeDatabase($config, 'DB');
$nodeTable = new NodeTable($config, 'node');
$nodeContainer->addChild($nodeTable);
$result = $nodeTable->getTriggers($dbi, 1);
self::assertCount(2, $result);
self::assertSame('foo2', $result[0]->name);
self::assertSame('foo3', $result[1]->name);
}
public function testGetIndexes(): void
{
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = true;
$config->set('MaxNavigationItems', 2);
$dbi = self::createMock(DatabaseInterface::class);
$resultStub = self::createMock(DummyResult::class);
$dbi->expects(self::once())
->method('tryQuery')
->with('SHOW INDEXES FROM `node` FROM `DB`')
->willReturn($resultStub);
$resultStub->expects(self::once())
->method('fetchAllAssoc')
->willReturnOnConsecutiveCalls(
[
['Key_name' => 'foo1'],
['Key_name' => 'foo2'],
['Key_name' => 'foo3'],
['Key_name' => 'foo4'],
],
);
$nodeContainer = new NodeDatabase($config, 'DB');
$nodeTable = new NodeTable($config, 'node');
$nodeContainer->addChild($nodeTable);
$result = $nodeTable->getIndexes($dbi, 1);
self::assertCount(2, $result);
self::assertSame('foo2', $result[0]->name);
self::assertSame('foo3', $result[1]->name);
}
}

View File

@ -295,7 +295,7 @@ final class NodeTest extends AbstractTestCase
"WHERE TRUE AND `SCHEMA_NAME` NOT REGEXP 'regexpHideDb' ",
$method->invoke($node, 'SCHEMA_NAME'),
);
unset($config->selectedServer['hide_db']);
$config->selectedServer['hide_db'] = '';
// When only_db directive is present and it's a single db
$config->selectedServer['only_db'] = 'stringOnlyDb';
@ -303,7 +303,7 @@ final class NodeTest extends AbstractTestCase
"WHERE TRUE AND ( `SCHEMA_NAME` LIKE 'stringOnlyDb' ) ",
$method->invoke($node, 'SCHEMA_NAME'),
);
unset($config->selectedServer['only_db']);
$config->selectedServer['only_db'] = '';
// When only_db directive is present and it's an array of dbs
$config->selectedServer['only_db'] = ['onlyDbOne', 'onlyDbTwo'];
@ -311,7 +311,7 @@ final class NodeTest extends AbstractTestCase
'WHERE TRUE AND ( `SCHEMA_NAME` LIKE \'onlyDbOne\' OR `SCHEMA_NAME` LIKE \'onlyDbTwo\' ) ',
$method->invoke($node, 'SCHEMA_NAME'),
);
unset($config->selectedServer['only_db']);
$config->selectedServer['only_db'] = '';
}
/**
@ -322,7 +322,7 @@ final class NodeTest extends AbstractTestCase
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = false;
$config->settings['NavigationTreeEnableGrouping'] = true;
$config->settings['FirstLevelNavigationItems'] = 20;
$config->set('FirstLevelNavigationItems', 20);
$config->settings['NavigationTreeDbSeparator'] = '_';
$expectedSql = 'SELECT `SCHEMA_NAME` ';
@ -361,7 +361,7 @@ final class NodeTest extends AbstractTestCase
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = false;
$config->settings['NavigationTreeEnableGrouping'] = false;
$config->settings['FirstLevelNavigationItems'] = 20;
$config->set('FirstLevelNavigationItems', 20);
$expectedSql = 'SELECT `SCHEMA_NAME` ';
$expectedSql .= 'FROM `INFORMATION_SCHEMA`.`SCHEMATA` ';
@ -386,7 +386,7 @@ final class NodeTest extends AbstractTestCase
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = true;
$config->settings['NavigationTreeEnableGrouping'] = true;
$config->settings['FirstLevelNavigationItems'] = 10;
$config->set('FirstLevelNavigationItems', 2);
$config->settings['NavigationTreeDbSeparator'] = '_';
$node = new Node($config, 'node');
@ -398,9 +398,9 @@ final class NodeTest extends AbstractTestCase
->method('tryQuery')
->with("SHOW DATABASES WHERE TRUE AND `Database` LIKE '%db%' ")
->willReturn($resultStub);
$resultStub->expects(self::exactly(3))
->method('fetchRow')
->willReturn(['0' => 'db'], ['0' => 'aa_db'], []);
$resultStub->expects(self::exactly(2))
->method('fetchValue')
->willReturn('db', 'aa_db', 'foo', '');
$dbi->expects(self::once())
->method('fetchSingleColumn')
@ -418,6 +418,40 @@ final class NodeTest extends AbstractTestCase
$node->getData(new UserPrivileges(), 0, 'db');
}
/**
* Tests when DisableIS is true and navigation tree grouping disabled.
*/
public function testGetDataWithDisabledISAndGroupingDisabled(): void
{
$config = Config::getInstance();
$config->selectedServer['DisableIS'] = true;
$config->settings['NavigationTreeEnableGrouping'] = false;
$config->set('FirstLevelNavigationItems', 2);
$node = new Node($config, 'node');
$resultStub = self::createMock(DummyResult::class);
$dbi = self::createMock(DatabaseInterface::class);
$dbi->expects(self::once())
->method('tryQuery')
->with("SHOW DATABASES WHERE TRUE AND `Database` LIKE '%db%' ")
->willReturn($resultStub);
$resultStub->expects(self::once())
->method('fetchAllColumn')
->willReturn(['db', 'aa_db', 'foo']);
$dbi->expects(self::once())->method('escapeMysqlWildcards')
->willReturnArgument(0);
$dbi->expects(self::once())->method('quoteString')
->willReturnCallback(static fn (string $string): string => "'" . $string . "'");
DatabaseInterface::$instance = $dbi;
$result = $node->getData(new UserPrivileges(), 0, 'db');
self::assertSame(['db', 'aa_db'], $result);
}
/**
* Tests when DisableIS is false and navigation tree grouping enabled.
*/