Node::getData()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.074

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 16
rs 10
ccs 5
cts 6
cp 0.8333
cc 4
nc 3
nop 5
crap 4.074
1
<?php
2
/**
3
 * Functionality for the navigation tree in the left frame
4
 */
5
6
declare(strict_types=1);
7
8
namespace PhpMyAdmin\Navigation\Nodes;
9
10
use PhpMyAdmin\Config;
11
use PhpMyAdmin\ConfigStorage\Features\NavigationItemsHidingFeature;
12
use PhpMyAdmin\ConfigStorage\RelationParameters;
0 ignored issues
show
Bug introduced by
The type PhpMyAdmin\ConfigStorage\RelationParameters was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use PhpMyAdmin\Dbal\ConnectionType;
14
use PhpMyAdmin\Dbal\DatabaseInterface;
15
use PhpMyAdmin\Html\Generator;
16
use PhpMyAdmin\Navigation\NodeType;
17
use PhpMyAdmin\UserPrivileges;
18
use PhpMyAdmin\Util;
19
20
use function __;
21
use function array_keys;
22
use function array_reverse;
23
use function array_slice;
24
use function base64_encode;
25
use function count;
26
use function implode;
27
use function in_array;
28
use function is_string;
29
use function preg_match;
30
use function sort;
31
use function sprintf;
32
use function str_starts_with;
33
use function strstr;
34
35
/**
36
 * The Node is the building block for the collapsible navigation tree
37
 */
38
class Node
39
{
40
    /**
41
     * @var string A non-unique identifier for the node
42
     *             This will never change after being assigned
43
     */
44
    public string $realName = '';
45
46
    /**
47
     * @var bool Whether to add a "display: none;" CSS
48
     *           rule to the node when rendering it
49
     */
50
    public bool $visible = false;
51
    /**
52
     * @var Node|null A reference to the parent object of
53
     *           this node, NULL for the root node.
54
     */
55
    public Node|null $parent = null;
56
    /**
57
     * @var Node[] An array of Node objects that are
58
     *             direct children of this node
59
     */
60
    public array $children = [];
61
    /**
62
     * @var string[] A string used to group nodes, or an array of strings
63
     *            Only relevant if the node is of type CONTAINER
64
     */
65
    public array $separators = [];
66
    /**
67
     * @var int How many time to recursively apply the grouping function
68
     *          Only relevant if the node is of type CONTAINER
69
     */
70
    public int $separatorDepth = 1;
71
72
    /**
73
     * For the IMG tag, used when rendering the node.
74
     */
75
    public Icon $icon;
76
77
    public Link $link;
78
79
    /** @var string Extra CSS classes for the node */
80
    public string $classes = '';
81
    /** @var bool Whether this node is a link for creating new objects */
82
    public bool $isNew = false;
83
    /**
84
     * @var int The position for the pagination of
85
     *          the branch at the second level of the tree
86
     */
87
    public int $pos2 = 0;
88
    /**
89
     * @var int The position for the pagination of
90
     *          the branch at the third level of the tree
91
     */
92
    public int $pos3 = 0;
93
94
    public string|null $urlParamName = null;
95
96
    /**
97
     * Initialises the class by setting the mandatory variables
98
     *
99
     * @param string   $name    A non-unique identifier for the node
100
     *                          This may be trimmed when grouping nodes
101
     * @param NodeType $type    Type of node, may be one of CONTAINER or OBJECT
102
     * @param bool     $isGroup Whether this object has been created while grouping nodes
103
     *                          Only relevant if the node is of type CONTAINER
104
     */
105 212
    public function __construct(
106
        protected readonly Config $config,
107
        public string $name = '',
108
        public readonly NodeType $type = NodeType::Object,
109
        public bool $isGroup = false,
110
    ) {
111 212
        $this->realName = $name;
112 212
        $this->icon = new Icon('', '', '');
113 212
        $this->link = new Link('', '', []);
114
    }
115
116
    /**
117
     * Instantiates a Node object that will be used only for "New db/table/etc.." objects
118
     *
119
     * @param string $name    An identifier for the new node
120
     * @param string $classes Extra CSS classes for the node
121
     */
122 36
    public function getInstanceForNewNode(
123
        string $name,
124
        string $classes,
125
    ): Node {
126 36
        $node = new Node($this->config, $name);
127 36
        $node->isNew = true;
128 36
        $node->classes = $classes;
129
130 36
        return $node;
131
    }
132
133
    /**
134
     * Adds a child node to this node
135
     *
136
     * @param Node $child A child node
137
     */
138 84
    public function addChild(Node $child): void
139
    {
140 84
        $this->children[] = $child;
141 84
        $child->parent = $this;
142
    }
143
144
    /**
145
     * Returns a child node given it's name
146
     *
147
     * @param string $name     The name of requested child
148
     * @param bool   $realName Whether to use the "realName"
149
     *                         instead of "name" in comparisons
150
     *
151
     * @return Node|null The requested child node or null,
152
     *                   if the requested node cannot be found
153
     */
154 8
    public function getChild(string $name, bool $realName = false): Node|null
155
    {
156 8
        foreach ($this->children as $child) {
157 8
            if ($realName) {
158 8
                if ($child->realName === $name) {
159 8
                    return $child;
160
                }
161 8
            } elseif ($child->name === $name && ! $child->isNew) {
162 8
                return $child;
163
            }
164
        }
165
166 4
        return null;
167
    }
168
169
    /**
170
     * Removes a child node from this node
171
     *
172
     * @param string $name The name of child to be removed
173
     */
174 4
    public function removeChild(string $name): void
175
    {
176 4
        foreach ($this->children as $key => $child) {
177 4
            if ($child->name === $name) {
178 4
                unset($this->children[$key]);
179 4
                break;
180
            }
181
        }
182
    }
183
184
    /**
185
     * Retrieves the parents for a node
186
     *
187
     * @param bool $self       Whether to include the Node itself in the results
188
     * @param bool $containers Whether to include nodes of type CONTAINER
189
     * @param bool $groups     Whether to include nodes which have $group == true
190
     *
191
     * @return list<Node> An array of parent Nodes
0 ignored issues
show
Bug introduced by
The type PhpMyAdmin\Navigation\Nodes\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
192
     */
193 20
    public function parents(bool $self = false, bool $containers = false, bool $groups = false): array
194
    {
195 20
        $parents = [];
196 20
        $parent = $self ? $this : $this->parent;
197
198 20
        while ($parent !== null) {
199 20
            if (($parent->type !== NodeType::Container || $containers) && (! $parent->isGroup || $groups)) {
200 20
                $parents[] = $parent;
201
            }
202
203 20
            $parent = $parent->parent;
204
        }
205
206 20
        return $parents;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $parents returns the type array|array<mixed,PhpMyA...\Navigation\Nodes\Node> which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
207
    }
208
209
    /**
210
     * Returns the actual parent of a node. If used twice on an index or columns
211
     * node, it will return the table and database nodes. The names of the returned
212
     * nodes can be used in SQL queries, etc...
213
     */
214 8
    public function getRealParent(): Node|false
215
    {
216 8
        $parent = $this->parent;
217
218 8
        while ($parent !== null) {
219 8
            if ($parent->type !== NodeType::Container && ! $parent->isGroup) {
220 8
                return $parent;
221
            }
222
223
            $parent = $parent->parent;
224
        }
225
226 4
        return false;
227
    }
228
229
    /**
230
     * This function checks if the node has children nodes associated with it
231
     *
232
     * @param bool $countEmptyContainers Whether to count empty child
233
     *                                   containers as valid children
234
     */
235 12
    public function hasChildren(bool $countEmptyContainers = true): bool
236
    {
237 12
        if ($countEmptyContainers) {
238 8
            return $this->children !== [];
239
        }
240
241 12
        foreach ($this->children as $child) {
242 12
            if ($child->type === NodeType::Object || $child->hasChildren(false)) {
243 12
                return true;
244
            }
245
        }
246
247 12
        return false;
248
    }
249
250
    /**
251
     * Returns true if the node has some siblings (other nodes on the same tree
252
     * level, in the same branch), false otherwise.
253
     * The only exception is for nodes on
254
     * the third level of the tree (columns and indexes), for which the function
255
     * always returns true. This is because we want to render the containers
256
     * for these nodes
257
     */
258 12
    public function hasSiblings(): bool
259
    {
260 12
        if ($this->parent === null) {
261 4
            return false;
262
        }
263
264 12
        $paths = $this->getPaths();
265 12
        if (count($paths['aPath_clean']) > 3) {
266 4
            return true;
267
        }
268
269 12
        foreach ($this->parent->children as $child) {
270 12
            if ($child !== $this && ($child->type === NodeType::Object || $child->hasChildren(false))) {
271 8
                return true;
272
            }
273
        }
274
275 12
        return false;
276
    }
277
278
    /**
279
     * Returns the actual path and the virtual paths for a node
280
     * both as clean arrays and base64 encoded strings
281
     *
282
     * @return array{aPath: string, aPath_clean: string[], vPath: string, vPath_clean: string[]}
283
     */
284 16
    public function getPaths(): array
285
    {
286 16
        $aPath = [];
287 16
        $aPathClean = [];
288 16
        foreach ($this->parents(true, true) as $parent) {
289 16
            $aPath[] = base64_encode($parent->realName);
290 16
            $aPathClean[] = $parent->realName;
291
        }
292
293 16
        $aPath = implode('.', array_reverse($aPath));
294 16
        $aPathClean = array_reverse($aPathClean);
295
296 16
        $vPath = [];
297 16
        $vPathClean = [];
298 16
        foreach ($this->parents(true, true, true) as $parent) {
299 16
            $vPath[] = base64_encode($parent->name);
300 16
            $vPathClean[] = $parent->name;
301
        }
302
303 16
        $vPath = implode('.', array_reverse($vPath));
304 16
        $vPathClean = array_reverse($vPathClean);
305
306 16
        return ['aPath' => $aPath, 'aPath_clean' => $aPathClean, 'vPath' => $vPath, 'vPath_clean' => $vPathClean];
307
    }
308
309
    /**
310
     * Returns the names of children of type $type present inside this container
311
     * This method is overridden by the PhpMyAdmin\Navigation\Nodes\NodeDatabase
312
     * and PhpMyAdmin\Navigation\Nodes\NodeTable classes
313
     *
314
     * @param string $type         The type of item we are looking for
315
     *                             ('tables', 'views', etc)
316
     * @param int    $pos          The offset of the list within the results
317
     * @param string $searchClause A string used to filter the results of the query
318
     *
319
     * @return mixed[]
320
     */
321 12
    public function getData(
322
        UserPrivileges $userPrivileges,
323
        RelationParameters $relationParameters,
0 ignored issues
show
Unused Code introduced by
The parameter $relationParameters is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

323
        /** @scrutinizer ignore-unused */ RelationParameters $relationParameters,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
324
        string $type,
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

324
        /** @scrutinizer ignore-unused */ string $type,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
325
        int $pos,
326
        string $searchClause = '',
327
    ): array {
328 12
        if (isset($this->config->selectedServer['DisableIS']) && ! $this->config->selectedServer['DisableIS']) {
329 8
            return $this->getDataFromInfoSchema($pos, $searchClause);
330
        }
331
332 4
        if ($userPrivileges->databasesToTest === false) {
333 4
            return $this->getDataFromShowDatabases($pos, $searchClause);
334
        }
335
336
        return $this->getDataFromShowDatabasesLike($userPrivileges, $pos, $searchClause);
337
    }
338
339
    /**
340
     * Returns the number of children of type $type present inside this container
341
     * This method is overridden by the PhpMyAdmin\Navigation\Nodes\NodeDatabase
342
     * and PhpMyAdmin\Navigation\Nodes\NodeTable classes
343
     *
344
     * @param string $type         The type of item we are looking for
345
     *                             ('tables', 'views', etc)
346
     * @param string $searchClause A string used to filter the results of the query
347
     */
348 12
    public function getPresence(UserPrivileges $userPrivileges, string $type = '', string $searchClause = ''): int
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

348
    public function getPresence(UserPrivileges $userPrivileges, /** @scrutinizer ignore-unused */ string $type = '', string $searchClause = ''): int

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
349
    {
350 12
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

350
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
351
        if (
352 12
            ! $this->config->settings['NavigationTreeEnableGrouping']
353 12
            || ! $this->config->settings['ShowDatabasesNavigationAsTree']
354
        ) {
355 4
            if (isset($this->config->selectedServer['DisableIS']) && ! $this->config->selectedServer['DisableIS']) {
356 4
                $query = 'SELECT COUNT(*) ';
357 4
                $query .= 'FROM INFORMATION_SCHEMA.SCHEMATA ';
358 4
                $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
359
360 4
                return (int) $dbi->fetchValue($query);
361
            }
362
363
            if ($userPrivileges->databasesToTest === false) {
364
                $query = 'SHOW DATABASES ';
365
                $query .= $this->getWhereClause('Database', $searchClause);
366
367
                return $this->queryAndGetNumRows($query);
368
            }
369
370
            $retval = 0;
371
            foreach ($this->getDatabasesToSearch($userPrivileges, $searchClause) as $db) {
372
                $query = 'SHOW DATABASES LIKE ' . $dbi->quoteString($db);
373
                $retval += $this->queryAndGetNumRows($query);
374
            }
375
376
            return $retval;
377
        }
378
379 8
        $dbSeparator = $this->config->settings['NavigationTreeDbSeparator'];
380 8
        if (! $this->config->selectedServer['DisableIS']) {
381 4
            $query = 'SELECT COUNT(*) ';
382 4
            $query .= 'FROM ( ';
383 4
            $query .= 'SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, ';
384 4
            $query .= "'" . $dbSeparator . "', 1) ";
385 4
            $query .= 'DB_first_level ';
386 4
            $query .= 'FROM INFORMATION_SCHEMA.SCHEMATA ';
387 4
            $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
388 4
            $query .= ') t ';
389
390 4
            return (int) $dbi->fetchValue($query);
391
        }
392
393 4
        if ($userPrivileges->databasesToTest !== false) {
394
            $prefixMap = [];
395
            foreach ($this->getDatabasesToSearch($userPrivileges, $searchClause) as $db) {
396
                $query = 'SHOW DATABASES LIKE ' . $dbi->quoteString($db);
397
                $handle = $dbi->tryQuery($query);
398
                if ($handle === false) {
399
                    continue;
400
                }
401
402
                while ($arr = $handle->fetchRow()) {
403
                    if ($this->isHideDb($arr[0])) {
404
                        continue;
405
                    }
406
407
                    $prefix = strstr($arr[0], $dbSeparator, true);
408
                    if ($prefix === false) {
409
                        $prefix = $arr[0];
410
                    }
411
412
                    $prefixMap[$prefix] = 1;
413
                }
414
            }
415
416
            return count($prefixMap);
417
        }
418
419 4
        $prefixMap = [];
420 4
        $query = 'SHOW DATABASES ';
421 4
        $query .= $this->getWhereClause('Database', $searchClause);
422 4
        $handle = $dbi->tryQuery($query);
423 4
        if ($handle !== false) {
424 4
            while ($arr = $handle->fetchRow()) {
425
                $prefix = strstr($arr[0], $dbSeparator, true);
0 ignored issues
show
Bug introduced by
It seems like $arr[0] can also be of type null; however, parameter $haystack of strstr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

425
                $prefix = strstr(/** @scrutinizer ignore-type */ $arr[0], $dbSeparator, true);
Loading history...
426
                if ($prefix === false) {
427
                    $prefix = $arr[0];
428
                }
429
430
                $prefixMap[$prefix] = 1;
431
            }
432
        }
433
434 4
        return count($prefixMap);
435
    }
436
437
    /**
438
     * Detemines whether a given database should be hidden according to 'hide_db'
439
     *
440
     * @param string $db database name
441
     */
442
    private function isHideDb(string $db): bool
443
    {
444
        return ! empty($this->config->selectedServer['hide_db'])
445
            && preg_match('/' . $this->config->selectedServer['hide_db'] . '/', $db) === 1;
446
    }
447
448
    /**
449
     * Get the list of databases for 'SHOW DATABASES LIKE' queries.
450
     * If a search clause is set it gets the highest priority while only_db gets
451
     * the next priority. In case both are empty list of databases determined by
452
     * GRANTs are used
453
     *
454
     * @param string $searchClause search clause
455
     *
456
     * @return mixed[] array of databases
457
     */
458
    private function getDatabasesToSearch(UserPrivileges $userPrivileges, string $searchClause): array
459
    {
460
        $databases = [];
461
        if ($searchClause !== '') {
462
            $databases = ['%' . DatabaseInterface::getInstance()->escapeMysqlWildcards($searchClause) . '%'];
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

462
            $databases = ['%' . /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance()->escapeMysqlWildcards($searchClause) . '%'];

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
463
        } elseif (! empty($this->config->selectedServer['only_db'])) {
464
            $databases = $this->config->selectedServer['only_db'];
465
        } elseif ($userPrivileges->databasesToTest !== false && $userPrivileges->databasesToTest !== []) {
466
            $databases = $userPrivileges->databasesToTest;
467
        }
468
469
        sort($databases);
470
471
        return $databases;
472
    }
473
474
    /**
475
     * Returns the WHERE clause depending on the $searchClause parameter
476
     * and the hide_db directive
477
     *
478
     * @param string $columnName   Column name of the column having database names
479
     * @param string $searchClause A string used to filter the results of the query
480
     */
481 28
    private function getWhereClause(string $columnName, string $searchClause = ''): string
482
    {
483 28
        $whereClause = 'WHERE TRUE ';
484 28
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

484
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
485 28
        if ($searchClause !== '') {
486 12
            $whereClause .= 'AND ' . Util::backquote($columnName)
487 12
                . ' LIKE ' . $dbi->quoteString('%' . $dbi->escapeMysqlWildcards($searchClause) . '%') . ' ';
488
        }
489
490 28
        if (! empty($this->config->selectedServer['hide_db'])) {
491 4
            $whereClause .= 'AND ' . Util::backquote($columnName)
492 4
                . ' NOT REGEXP ' . $dbi->quoteString($this->config->selectedServer['hide_db']) . ' ';
493
        }
494
495 28
        if (! empty($this->config->selectedServer['only_db'])) {
496 4
            if (is_string($this->config->selectedServer['only_db'])) {
497 4
                $this->config->selectedServer['only_db'] = [$this->config->selectedServer['only_db']];
498
            }
499
500 4
            $whereClause .= 'AND (';
501 4
            $subClauses = [];
502 4
            foreach ($this->config->selectedServer['only_db'] as $eachOnlyDb) {
503 4
                $subClauses[] = ' ' . Util::backquote($columnName)
504 4
                    . ' LIKE ' . $dbi->quoteString($eachOnlyDb) . ' ';
505
            }
506
507 4
            $whereClause .= implode('OR', $subClauses) . ') ';
508
        }
509
510 28
        return $whereClause;
511
    }
512
513
    /**
514
     * Returns HTML for control buttons displayed infront of a node
515
     *
516
     * @return string HTML for control buttons
517
     */
518
    public function getHtmlForControlButtons(NavigationItemsHidingFeature|null $navigationItemsHidingFeature): string
519
    {
520
        return '';
521
    }
522
523
    /**
524
     * Returns CSS classes for a node
525
     *
526
     * @param bool $match Whether the node matched loaded tree
527
     *
528
     * @return string with html classes.
529
     */
530
    public function getCssClasses(bool $match): string
531
    {
532
        if (! $this->config->settings['NavigationTreeEnableExpansion']) {
533
            return '';
534
        }
535
536
        $result = ['expander'];
537
538
        if ($this->isGroup || $match) {
539
            $result[] = 'loaded';
540
        }
541
542
        if ($this->type === NodeType::Container) {
543
            $result[] = 'container';
544
        }
545
546
        return implode(' ', $result);
547
    }
548
549
    /**
550
     * Returns icon for the node
551
     *
552
     * @param bool $match Whether the node matched loaded tree
553
     *
554
     * @return string with image name
555
     */
556
    public function getIcon(bool $match): string
557
    {
558
        if (! $this->config->settings['NavigationTreeEnableExpansion']) {
559
            return '';
560
        }
561
562
        if ($match) {
563
            $this->visible = true;
564
565
            return Generator::getImage('b_minus');
566
        }
567
568
        return Generator::getImage('b_plus', __('Expand/Collapse'));
569
    }
570
571
    /**
572
     * Gets the count of hidden elements for each database
573
     *
574
     * @return mixed[]|null array containing the count of hidden elements for each database
575
     */
576
    public function getNavigationHidingData(NavigationItemsHidingFeature|null $navigationItemsHidingFeature): array|null
577
    {
578
        if ($navigationItemsHidingFeature !== null) {
579
            $navTable = Util::backquote($navigationItemsHidingFeature->database)
580
                . '.' . Util::backquote($navigationItemsHidingFeature->navigationHiding);
581
            $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

581
            $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
582
            $sqlQuery = 'SELECT `db_name`, COUNT(*) AS `count` FROM ' . $navTable
583
                . ' WHERE `username`='
584
                . $dbi->quoteString($this->config->selectedServer['user'])
585
                . ' GROUP BY `db_name`';
586
587
            return $dbi->fetchResult($sqlQuery, 'db_name', 'count', ConnectionType::ControlUser);
588
        }
589
590
        return null;
591
    }
592
593
    /**
594
     * @param int    $pos          The offset of the list within the results.
595
     * @param string $searchClause A string used to filter the results of the query.
596
     *
597
     * @return list<string|null>
598
     */
599 8
    private function getDataFromInfoSchema(int $pos, string $searchClause): array
600
    {
601 8
        $maxItems = $this->config->settings['FirstLevelNavigationItems'];
602 8
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

602
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
603
        if (
604 8
            ! $this->config->settings['NavigationTreeEnableGrouping']
605 8
            || ! $this->config->settings['ShowDatabasesNavigationAsTree']
606
        ) {
607 4
            $query = sprintf(
608 4
                'SELECT `SCHEMA_NAME` FROM `INFORMATION_SCHEMA`.`SCHEMATA` %sORDER BY `SCHEMA_NAME` LIMIT %d, %d',
609 4
                $this->getWhereClause('SCHEMA_NAME', $searchClause),
610 4
                $pos,
611 4
                $maxItems,
612 4
            );
613
614 4
            return $dbi->fetchSingleColumn($query);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dbi->fetchSingleColumn($query) returns the type array|array<integer,null|string> which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
615
        }
616
617 4
        $dbSeparator = $this->config->settings['NavigationTreeDbSeparator'];
618 4
        $query = sprintf(
619 4
            'SELECT `SCHEMA_NAME` FROM `INFORMATION_SCHEMA`.`SCHEMATA`, (SELECT DB_first_level'
620 4
                . ' FROM ( SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, %1$s, 1) DB_first_level'
621 4
                . ' FROM INFORMATION_SCHEMA.SCHEMATA %2$s) t'
622 4
                . ' ORDER BY DB_first_level ASC LIMIT %3$d, %4$d) t2'
623 4
                . ' %2$sAND 1 = LOCATE(CONCAT(DB_first_level, %1$s),'
624 4
                . ' CONCAT(SCHEMA_NAME, %1$s)) ORDER BY SCHEMA_NAME ASC',
625 4
            $dbi->quoteString($dbSeparator),
626 4
            $this->getWhereClause('SCHEMA_NAME', $searchClause),
627 4
            $pos,
628 4
            $maxItems,
629 4
        );
630
631 4
        return $dbi->fetchSingleColumn($query);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dbi->fetchSingleColumn($query) returns the type array|array<integer,null|string> which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
632
    }
633
634
    /**
635
     * @param int    $pos          The offset of the list within the results.
636
     * @param string $searchClause A string used to filter the results of the query.
637
     *
638
     * @return list<string|null>
639
     */
640 4
    private function getDataFromShowDatabases(int $pos, string $searchClause): array
641
    {
642 4
        $maxItems = $this->config->settings['FirstLevelNavigationItems'];
643 4
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

643
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
644
        if (
645 4
            ! $this->config->settings['NavigationTreeEnableGrouping']
646 4
            || ! $this->config->settings['ShowDatabasesNavigationAsTree']
647
        ) {
648
            $handle = $dbi->tryQuery(sprintf(
649
                'SHOW DATABASES %s',
650
                $this->getWhereClause('Database', $searchClause),
651
            ));
652
            if ($handle === false) {
653
                return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
654
            }
655
656
            $count = 0;
657
            if (! $handle->seek($pos)) {
658
                return [];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array() returns the type array which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
659
            }
660
661
            $retval = [];
662
            while ($arr = $handle->fetchRow()) {
663
                if ($count >= $maxItems) {
664
                    break;
665
                }
666
667
                $retval[] = $arr[0];
668
                $count++;
669
            }
670
671
            return $retval;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $retval returns the type array|array<mixed,null|string> which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
672
        }
673
674 4
        $dbSeparator = $this->config->settings['NavigationTreeDbSeparator'];
675 4
        $handle = $dbi->tryQuery(sprintf(
676 4
            'SHOW DATABASES %s',
677 4
            $this->getWhereClause('Database', $searchClause),
678 4
        ));
679 4
        $prefixes = [];
680 4
        if ($handle !== false) {
681 4
            $prefixMap = [];
682 4
            $total = $pos + $maxItems;
683 4
            while ($arr = $handle->fetchRow()) {
684 4
                $prefix = strstr($arr[0], $dbSeparator, true);
0 ignored issues
show
Bug introduced by
It seems like $arr[0] can also be of type null; however, parameter $haystack of strstr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

684
                $prefix = strstr(/** @scrutinizer ignore-type */ $arr[0], $dbSeparator, true);
Loading history...
685 4
                if ($prefix === false) {
686 4
                    $prefix = $arr[0];
687
                }
688
689 4
                $prefixMap[$prefix] = 1;
690 4
                if (count($prefixMap) == $total) {
691
                    break;
692
                }
693
            }
694
695 4
            $prefixes = array_slice(array_keys($prefixMap), $pos);
696
        }
697
698 4
        $subClauses = [];
699 4
        foreach ($prefixes as $prefix) {
700 4
            $subClauses[] = sprintf(
701 4
                ' LOCATE(%s, CONCAT(`Database`, %s)) = 1 ',
702 4
                $dbi->quoteString($prefix . $dbSeparator),
703 4
                $dbi->quoteString($dbSeparator),
704 4
            );
705
        }
706
707 4
        $query = sprintf(
708 4
            'SHOW DATABASES %sAND (%s)',
709 4
            $this->getWhereClause('Database', $searchClause),
710 4
            implode('OR', $subClauses),
711 4
        );
712
713 4
        return $dbi->fetchSingleColumn($query);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dbi->fetchSingleColumn($query) returns the type array|array<integer,null|string> which is incompatible with the documented return type PhpMyAdmin\Navigation\Nodes\list.
Loading history...
714
    }
715
716
    /**
717
     * @param int    $pos          The offset of the list within the results.
718
     * @param string $searchClause A string used to filter the results of the query.
719
     *
720
     * @return list<string|null>
721
     */
722
    private function getDataFromShowDatabasesLike(UserPrivileges $userPrivileges, int $pos, string $searchClause): array
723
    {
724
        $maxItems = $this->config->settings['FirstLevelNavigationItems'];
725
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

725
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
726
        if (
727
            ! $this->config->settings['NavigationTreeEnableGrouping']
728
            || ! $this->config->settings['ShowDatabasesNavigationAsTree']
729
        ) {
730
            $retval = [];
731
            $count = 0;
732
            foreach ($this->getDatabasesToSearch($userPrivileges, $searchClause) as $db) {
733
                $handle = $dbi->tryQuery(sprintf('SHOW DATABASES LIKE %s', $dbi->quoteString($db)));
734
                if ($handle === false) {
735
                    continue;
736
                }
737
738
                while ($arr = $handle->fetchRow()) {
739
                    if ($this->isHideDb($arr[0])) {
740
                        continue;
741
                    }
742
743
                    if (in_array($arr[0], $retval, true)) {
744
                        continue;
745
                    }
746
747
                    if ($pos <= 0 && $count < $maxItems) {
748
                        $retval[] = $arr[0];
749
                        $count++;
750
                    }
751
752
                    $pos--;
753
                }
754
            }
755
756
            sort($retval);
757
758
            return $retval;
759
        }
760
761
        $dbSeparator = $this->config->settings['NavigationTreeDbSeparator'];
762
        $retval = [];
763
        $prefixMap = [];
764
        $total = $pos + $maxItems;
765
        foreach ($this->getDatabasesToSearch($userPrivileges, $searchClause) as $db) {
766
            $handle = $dbi->tryQuery(sprintf('SHOW DATABASES LIKE %s', $dbi->quoteString($db)));
767
            if ($handle === false) {
768
                continue;
769
            }
770
771
            while ($arr = $handle->fetchRow()) {
772
                if ($this->isHideDb($arr[0])) {
773
                    continue;
774
                }
775
776
                $prefix = strstr($arr[0], $dbSeparator, true);
777
                if ($prefix === false) {
778
                    $prefix = $arr[0];
779
                }
780
781
                $prefixMap[$prefix] = 1;
782
                if (count($prefixMap) == $total) {
783
                    break 2;
784
                }
785
            }
786
        }
787
788
        $prefixes = array_slice(array_keys($prefixMap), $pos);
789
790
        foreach ($this->getDatabasesToSearch($userPrivileges, $searchClause) as $db) {
791
            $handle = $dbi->tryQuery(sprintf('SHOW DATABASES LIKE %s', $dbi->quoteString($db)));
792
            if ($handle === false) {
793
                continue;
794
            }
795
796
            while ($arr = $handle->fetchRow()) {
797
                if ($this->isHideDb($arr[0])) {
798
                    continue;
799
                }
800
801
                if (in_array($arr[0], $retval, true)) {
802
                    continue;
803
                }
804
805
                foreach ($prefixes as $prefix) {
806
                    $startsWith = str_starts_with($arr[0] . $dbSeparator, $prefix . $dbSeparator);
807
                    if ($startsWith) {
808
                        $retval[] = $arr[0];
809
                        break;
810
                    }
811
                }
812
            }
813
        }
814
815
        sort($retval);
816
817
        return $retval;
818
    }
819
820
    /**
821
     * returns the number of rows returned by last query
822
     * used with tryQuery as it accepts false
823
     */
824
    protected function queryAndGetNumRows(string $query): int
825
    {
826
        $dbi = DatabaseInterface::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Dbal\DatabaseInterface::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

826
        $dbi = /** @scrutinizer ignore-deprecated */ DatabaseInterface::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
827
        $result = $dbi->tryQuery($query);
828
829
        if ($result === false) {
830
            return 0;
831
        }
832
833
        return (int) $result->numRows();
834
    }
835
}
836