Translated using Weblate (Lithuanian)
[phpmyadmin.git] / test / classes / UtilTest.php
blobbeedb613e6b34de25855239cd5cfcb62d932ec9f
1 <?php
3 declare(strict_types=1);
5 namespace PhpMyAdmin\Tests;
7 use PhpMyAdmin\Core;
8 use PhpMyAdmin\DatabaseInterface;
9 use PhpMyAdmin\FieldMetadata;
10 use PhpMyAdmin\MoTranslator\Loader;
11 use PhpMyAdmin\ResponseRenderer;
12 use PhpMyAdmin\SqlParser\Context;
13 use PhpMyAdmin\SqlParser\Token;
14 use PhpMyAdmin\Util;
15 use PhpMyAdmin\Utils\SessionCache;
16 use PhpMyAdmin\Version;
18 use function __;
19 use function _setlocale;
20 use function count;
21 use function date_default_timezone_get;
22 use function date_default_timezone_set;
23 use function file_exists;
24 use function floatval;
25 use function htmlspecialchars;
26 use function ini_get;
27 use function ini_set;
28 use function str_repeat;
29 use function str_replace;
30 use function strlen;
31 use function trim;
33 use const LC_ALL;
34 use const MYSQLI_NUM_FLAG;
35 use const MYSQLI_PRI_KEY_FLAG;
36 use const MYSQLI_TYPE_BIT;
37 use const MYSQLI_TYPE_GEOMETRY;
38 use const MYSQLI_TYPE_LONG;
39 use const MYSQLI_TYPE_SHORT;
40 use const MYSQLI_TYPE_STRING;
41 use const MYSQLI_UNIQUE_KEY_FLAG;
43 /**
44 * @covers \PhpMyAdmin\Util
46 class UtilTest extends AbstractTestCase
48 /**
49 * init data for the test
51 protected function setUp(): void
53 parent::setUp();
54 parent::setLanguage();
55 parent::setTheme();
58 /**
59 * Test for listPHPExtensions
61 * @requires extension mysqli
62 * @requires extension curl
63 * @requires extension mbstring
65 public function testListPHPExtensions(): void
67 $this->assertSame(
69 'mysqli',
70 'curl',
71 'mbstring',
73 Util::listPHPExtensions()
77 public function testGetUniqueCondition(): void
79 $GLOBALS['db'] = 'db';
80 $GLOBALS['cfg']['Server']['DisableIS'] = false;
82 $actual = Util::getUniqueCondition(0, [], []);
83 $this->assertEquals(['', false, []], $actual);
85 $actual = Util::getUniqueCondition(0, [], [], true);
86 $this->assertEquals(['', true, []], $actual);
89 public function testGetUniqueConditionWithMultipleFields(): void
91 $meta = [
92 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
93 'name' => 'field1',
94 'table' => 'table',
95 'orgtable' => 'table',
96 ]),
97 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
98 'name' => 'field2',
99 'table' => 'table',
100 'orgtable' => 'table',
102 new FieldMetadata(MYSQLI_TYPE_SHORT, MYSQLI_NUM_FLAG, (object) [
103 'name' => 'field3',
104 'table' => 'table',
105 'orgtable' => 'table',
107 new FieldMetadata(MYSQLI_TYPE_LONG, MYSQLI_NUM_FLAG, (object) [
108 'name' => 'field4',
109 'table' => 'table',
110 'orgtable' => 'table',
112 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
113 'name' => 'field5',
114 'table' => 'table',
115 'orgtable' => 'table',
116 'charsetnr' => 63, // binary
118 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
119 'name' => 'field6',
120 'table' => 'table',
121 'orgtable' => 'table',
122 'charsetnr' => 63, // binary
124 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
125 'name' => 'field7',
126 'table' => 'table',
127 'orgtable' => 'table',
128 'numeric' => false,
129 'type' => 'blob',
130 'charsetnr' => 32, // armscii8_general_ci
132 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
133 'name' => 'field8',
134 'table' => 'table',
135 'orgtable' => 'table',
136 'numeric' => false,
137 'type' => 'blob',
138 'charsetnr' => 48, // latin1_general_ci
140 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
141 'name' => 'field9',
142 'table' => 'table',
143 'orgtable' => 'table',
144 'numeric' => false,
145 'type' => 'blob',
146 'charsetnr' => 63, // binary
148 new FieldMetadata(MYSQLI_TYPE_GEOMETRY, 0, (object) [
149 'name' => 'field10',
150 'table' => 'table',
151 'orgtable' => 'table',
153 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
154 'name' => 'field11',
155 'table' => 'table2',
156 'orgtable' => 'table2',
158 new FieldMetadata(MYSQLI_TYPE_BIT, 0, (object) [
159 'name' => 'field12',
160 'table' => 'table',
161 'orgtable' => 'table',
162 'length' => 4,
166 $actual = Util::getUniqueCondition(count($meta), $meta, [
167 null,
168 'value\'s',
169 123456,
170 123.456,
171 'value',
172 str_repeat('*', 1001),
173 'value',
174 'value',
175 'value',
176 'value',
177 'value',
178 0x1,
179 ], false, 'table');
180 $this->assertEquals(
182 '`table`.`field1` IS NULL AND `table`.`field2` = \'value\\\'s\' AND `table`.`field3` = 123456'
183 . ' AND `table`.`field4` = 123.456 AND `table`.`field5` = CAST(0x76616c7565 AS BINARY)'
184 . ' AND `table`.`field7` = \'value\' AND `table`.`field8` = \'value\''
185 . ' AND `table`.`field9` = CAST(0x76616c7565 AS BINARY)'
186 . ' AND `table`.`field10` =0x76616c7565 AND'
187 . ' AND `table`.`field12` = b\'0001\'',
188 false,
190 '`table`.`field1`' => 'IS NULL',
191 '`table`.`field2`' => '= \'value\\\'s\'',
192 '`table`.`field3`' => '= 123456',
193 '`table`.`field4`' => '= 123.456',
194 '`table`.`field5`' => '= CAST(0x76616c7565 AS BINARY)',
195 '`table`.`field7`' => '= \'value\'',
196 '`table`.`field8`' => '= \'value\'',
197 '`table`.`field9`' => '= CAST(0x76616c7565 AS BINARY)',
198 '`table`.`field10`' => '',
199 '`table`.`field12`' => '= b\'0001\'',
202 $actual
206 public function testGetUniqueConditionWithSingleBigBinaryField(): void
208 $meta = [
209 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
210 'name' => 'field',
211 'table' => 'table',
212 'orgtable' => 'table',
213 'charsetnr' => 63, // binary
217 $actual = Util::getUniqueCondition(1, $meta, [str_repeat('*', 1001)]);
218 $this->assertEquals(
219 ['CHAR_LENGTH(`table`.`field`) = 1001', false, ['`table`.`field`' => ' = 1001']],
220 $actual
224 public function testGetUniqueConditionWithPrimaryKey(): void
226 $meta = [
227 new FieldMetadata(MYSQLI_TYPE_LONG, MYSQLI_PRI_KEY_FLAG | MYSQLI_NUM_FLAG, (object) [
228 'name' => 'id',
229 'table' => 'table',
230 'orgtable' => 'table',
232 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
233 'name' => 'field',
234 'table' => 'table',
235 'orgtable' => 'table',
239 $actual = Util::getUniqueCondition(count($meta), $meta, [1, 'value']);
240 $this->assertEquals(['`table`.`id` = 1', true, ['`table`.`id`' => '= 1']], $actual);
243 public function testGetUniqueConditionWithUniqueKey(): void
245 $meta = [
246 new FieldMetadata(MYSQLI_TYPE_STRING, MYSQLI_UNIQUE_KEY_FLAG, (object) [
247 'name' => 'id',
248 'table' => 'table',
249 'orgtable' => 'table',
251 new FieldMetadata(MYSQLI_TYPE_STRING, 0, (object) [
252 'name' => 'field',
253 'table' => 'table',
254 'orgtable' => 'table',
258 $actual = Util::getUniqueCondition(count($meta), $meta, ['unique', 'value']);
259 $this->assertEquals(['`table`.`id` = \'unique\'', true, ['`table`.`id`' => '= \'unique\'']], $actual);
263 * Test for Page Selector
265 public function testPageSelector(): void
267 $this->assertStringContainsString(
268 '<select class="pageselector ajax" name="pma" >',
269 Util::pageselector('pma', 3)
272 // If pageNow > nbTotalPage, show the pageNow number to avoid confusion
273 $this->assertStringContainsString(
274 '<option selected="selected" style="font-weight: bold" value="297">100</option>',
275 Util::pageselector('pma', 3, 100, 50)
280 * Test for getCharsetQueryPart
282 * @param string $collation Collation
283 * @param string $expected Expected Charset Query
285 * @dataProvider charsetQueryData
287 public function testGenerateCharsetQueryPart(string $collation, string $expected): void
289 $this->assertEquals(
290 $expected,
291 Util::getCharsetQueryPart($collation)
296 * Data Provider for testgetCharsetQueryPart
298 * @return array test data
300 public function charsetQueryData(): array
302 return [
304 'a_b_c_d',
305 ' CHARSET=a COLLATE a_b_c_d',
308 'a_',
309 ' CHARSET=a COLLATE a_',
312 'a',
313 ' CHARSET=a',
319 * Test for random generation
321 public function testGenerateRandom(): void
323 $this->assertEquals(32, strlen(Util::generateRandom(32)));
324 $this->assertEquals(16, strlen(Util::generateRandom(16)));
327 public function testClearUserCache(): void
329 $GLOBALS['server'] = 'server';
330 SessionCache::set('is_superuser', 'yes');
331 $this->assertEquals('yes', $_SESSION['cache']['server_server']['is_superuser']);
333 Util::clearUserCache();
334 $this->assertArrayNotHasKey('is_superuser', $_SESSION['cache']['server_server']);
337 public function testCheckParameterMissing(): void
339 parent::setGlobalConfig();
340 $_REQUEST = [];
341 $GLOBALS['text_dir'] = 'ltr';
342 $GLOBALS['PMA_PHP_SELF'] = Core::getenv('PHP_SELF');
343 $GLOBALS['db'] = 'db';
344 $GLOBALS['table'] = 'table';
345 $GLOBALS['server'] = 1;
346 $GLOBALS['cfg']['ServerDefault'] = 1;
347 $GLOBALS['cfg']['AllowThirdPartyFraming'] = false;
348 ResponseRenderer::getInstance()->setAjax(false);
350 $this->expectOutputRegex('/Missing parameter: field/');
352 Util::checkParameters(
354 'db',
355 'table',
356 'field',
361 public function testCheckParameter(): void
363 parent::setGlobalConfig();
364 $GLOBALS['cfg'] = ['ServerDefault' => 1];
365 $GLOBALS['text_dir'] = 'ltr';
366 $GLOBALS['PMA_PHP_SELF'] = Core::getenv('PHP_SELF');
367 $GLOBALS['db'] = 'dbDatabase';
368 $GLOBALS['table'] = 'tblTable';
369 $GLOBALS['field'] = 'test_field';
370 $GLOBALS['sql_query'] = 'SELECT * FROM tblTable;';
372 $this->expectOutputString('');
373 Util::checkParameters(
375 'db',
376 'table',
377 'field',
378 'sql_query',
384 * Test for Util::convertBitDefaultValue
386 * @param string|null $bit Value
387 * @param string $val Expected value
389 * @dataProvider providerConvertBitDefaultValue
391 public function testConvertBitDefaultValue(?string $bit, string $val): void
393 $this->assertEquals(
394 $val,
395 Util::convertBitDefaultValue($bit)
400 * Provider for testConvertBitDefaultValue
402 * @return array
404 public function providerConvertBitDefaultValue(): array
406 return [
408 null,
412 "b'",
416 "b'01'",
417 '01',
420 "b'010111010'",
421 '010111010',
423 'database name starting with b' => [
424 'big database',
425 'big database',
427 "database name containing b'" => [
428 "a b'ig database",
429 "a b'ig database",
431 'database name in single quotes' => [
432 "'a*database*name'",
433 "'a*database*name'",
435 "database name with multiple b'" => [
436 "b'ens datab'ase'",
437 "b'ens datab'ase'",
443 * data provider for testEscapeMysqlWildcards and testUnescapeMysqlWildcards
445 * @return array
447 public function providerUnEscapeMysqlWildcards(): array
449 return [
451 '\_test',
452 '_test',
455 '\_\\',
456 '_\\',
459 '\\_\%',
460 '_%',
463 '\\\_',
464 '\_',
467 '\\\_\\\%',
468 '\_\%',
471 '\_\\%\_\_\%',
472 '_%__%',
475 '\%\_',
476 '%_',
479 '\\\%\\\_',
480 '\%\_',
486 * PhpMyAdmin\Util::escapeMysqlWildcards tests
488 * @param string $a Expected value
489 * @param string $b String to escape
491 * @dataProvider providerUnEscapeMysqlWildcards
493 public function testEscapeMysqlWildcards(string $a, string $b): void
495 $this->assertEquals(
497 Util::escapeMysqlWildcards($b)
502 * PhpMyAdmin\Util::unescapeMysqlWildcards tests
504 * @param string $a String to unescape
505 * @param string $b Expected value
507 * @dataProvider providerUnEscapeMysqlWildcards
509 public function testUnescapeMysqlWildcards(string $a, string $b): void
511 $this->assertEquals(
513 Util::unescapeMysqlWildcards($a)
518 * Test case for expanding strings
520 * @param string $in string to evaluate
521 * @param string $out expected output
523 * @dataProvider providerExpandUserString
525 public function testExpandUserString(string $in, string $out): void
527 parent::setGlobalConfig();
528 $GLOBALS['cfg'] = [
529 'Server' => [
530 'host' => 'host&',
531 'verbose' => 'verbose',
534 $GLOBALS['db'] = 'database';
535 $GLOBALS['table'] = 'table';
537 $this->assertEquals(
538 $out,
539 Util::expandUserString($in)
542 $this->assertEquals(
543 htmlspecialchars($out),
544 Util::expandUserString(
545 $in,
546 'htmlspecialchars'
552 * Data provider for testExpandUserString
554 * @return array
556 public function providerExpandUserString(): array
558 return [
560 '@SERVER@',
561 'host&',
564 '@VSERVER@',
565 'verbose',
568 '@DATABASE@',
569 'database',
572 '@TABLE@',
573 'table',
576 '@IGNORE@',
577 '@IGNORE@',
580 '@PHPMYADMIN@',
581 'phpMyAdmin ' . Version::VERSION,
587 * Test case for parsing SHOW COLUMNS output
589 * @param string $in Column specification
590 * @param array $out Expected value
592 * @dataProvider providerExtractColumnSpec
594 public function testExtractColumnSpec(string $in, array $out): void
596 $GLOBALS['cfg']['LimitChars'] = 1000;
598 $this->assertEquals(
599 $out,
600 Util::extractColumnSpec($in)
605 * Data provider for testExtractColumnSpec
607 * @return array
609 public function providerExtractColumnSpec(): array
611 return [
613 "SET('a','b')",
615 'type' => 'set',
616 'print_type' => "set('a', 'b')",
617 'binary' => false,
618 'unsigned' => false,
619 'zerofill' => false,
620 'spec_in_brackets' => "'a','b'",
621 'enum_set_values' => [
622 'a',
623 'b',
625 'attribute' => ' ',
626 'can_contain_collation' => true,
627 'displayed_type' => "set('a', 'b')",
631 "SET('\'a','b')",
633 'type' => 'set',
634 'print_type' => "set('\'a', 'b')",
635 'binary' => false,
636 'unsigned' => false,
637 'zerofill' => false,
638 'spec_in_brackets' => "'\'a','b'",
639 'enum_set_values' => [
640 "'a",
641 'b',
643 'attribute' => ' ',
644 'can_contain_collation' => true,
645 'displayed_type' => "set('\'a', 'b')",
649 "SET('''a','b')",
651 'type' => 'set',
652 'print_type' => "set('''a', 'b')",
653 'binary' => false,
654 'unsigned' => false,
655 'zerofill' => false,
656 'spec_in_brackets' => "'''a','b'",
657 'enum_set_values' => [
658 "'a",
659 'b',
661 'attribute' => ' ',
662 'can_contain_collation' => true,
663 'displayed_type' => "set('''a', 'b')",
667 "ENUM('a&b', 'b''c\\'d', 'e\\\\f')",
669 'type' => 'enum',
670 'print_type' => "enum('a&b', 'b''c\\'d', 'e\\\\f')",
671 'binary' => false,
672 'unsigned' => false,
673 'zerofill' => false,
674 'spec_in_brackets' => "'a&b', 'b''c\\'d', 'e\\\\f'",
675 'enum_set_values' => [
676 'a&b',
677 'b\'c\'d',
678 'e\\f',
680 'attribute' => ' ',
681 'can_contain_collation' => true,
682 'displayed_type' => "enum('a&amp;b', 'b''c\\'d', 'e\\\\f')",
686 'INT UNSIGNED zerofill',
688 'type' => 'int',
689 'print_type' => 'int',
690 'binary' => false,
691 'unsigned' => true,
692 'zerofill' => true,
693 'spec_in_brackets' => '',
694 'enum_set_values' => [],
695 'attribute' => 'UNSIGNED ZEROFILL',
696 'can_contain_collation' => false,
697 'displayed_type' => 'int',
701 'VARCHAR(255)',
703 'type' => 'varchar',
704 'print_type' => 'varchar(255)',
705 'binary' => false,
706 'unsigned' => false,
707 'zerofill' => false,
708 'spec_in_brackets' => '255',
709 'enum_set_values' => [],
710 'attribute' => ' ',
711 'can_contain_collation' => true,
712 'displayed_type' => 'varchar(255)',
716 'VARBINARY(255)',
718 'type' => 'varbinary',
719 'print_type' => 'varbinary(255)',
720 'binary' => false,
721 'unsigned' => false,
722 'zerofill' => false,
723 'spec_in_brackets' => '255',
724 'enum_set_values' => [],
725 'attribute' => ' ',
726 'can_contain_collation' => false,
727 'displayed_type' => 'varbinary(255)',
734 * Test for Util::extractValueFromFormattedSize
736 * @param int|string $size Size
737 * @param int|float $expected Expected value (float on some cpu architectures)
739 * @dataProvider providerExtractValueFromFormattedSize
741 public function testExtractValueFromFormattedSize($size, $expected): void
743 $this->assertEquals(
744 $expected,
745 Util::extractValueFromFormattedSize($size)
750 * Data provider for testExtractValueFromFormattedSize
752 * @return array
754 public function providerExtractValueFromFormattedSize(): array
756 return [
758 100,
762 '10GB',
763 10737418240,
766 '15MB',
767 15728640,
770 '256K',
771 262144,
777 * format byte test, globals are defined
779 * @param float|int|string $a Value to format
780 * @param int $b Sensitiveness
781 * @param int $c Number of decimals to retain
782 * @param array $e Expected value
784 * @dataProvider providerFormatByteDown
786 public function testFormatByteDown($a, int $b, int $c, array $e): void
788 $result = Util::formatByteDown($a, $b, $c);
789 $this->assertIsArray($result);
790 $result[0] = trim($result[0]);
791 $this->assertSame($e, $result);
795 * format byte down data provider
797 * @return array
799 public function providerFormatByteDown(): array
801 return [
803 '0',
807 '0',
808 __('B'),
812 'A4',
816 '0',
817 __('B'),
825 '10',
826 __('B'),
830 100,
834 '0',
835 __('KiB'),
839 100,
843 '100',
844 __('B'),
848 100,
852 '0.10',
853 __('KiB'),
857 1034,
861 '1.01',
862 __('KiB'),
866 100233,
870 '97.884',
871 __('KiB'),
875 '100233',
879 '97.884',
880 __('KiB'),
884 '102400K',
888 '100.000',
889 __('KiB'),
893 '102401K',
897 '100.001',
898 __('KiB'),
902 '153600K',
906 '150.000',
907 __('KiB'),
911 '153600K',
915 '150',
916 __('KiB'),
920 102400 * 1024,
924 '100',
925 __('MiB'),
929 2206451,
933 '2.10',
934 __('MiB'),
938 21474836480,
942 '20',
943 __('GiB'),
947 floatval(52) + floatval(2048),
951 '2.1',
952 'KiB',
956 '' . (floatval(52) + floatval(2048)),
960 '2.1',
961 'KiB',
968 * Core test for formatNumber
970 * @param float|int|string $a Value to format
971 * @param int $b Sensitiveness
972 * @param int $c Number of decimals to retain
973 * @param string $d Expected value
975 private function assertFormatNumber($a, int $b, int $c, string $d): void
977 $this->assertEquals(
979 (string) Util::formatNumber(
983 false
989 * format number test, globals are defined
991 * @param float|int|string $a Value to format
992 * @param int $b Sensitiveness
993 * @param int $c Number of decimals to retain
994 * @param string $d Expected value
996 * @dataProvider providerFormatNumber
998 public function testFormatNumber($a, int $b, int $c, string $d): void
1000 $this->assertFormatNumber($a, $b, $c, $d);
1002 // Test with various precisions
1003 $old_precision = (string) ini_get('precision');
1004 try {
1005 ini_set('precision', '20');
1006 $this->assertFormatNumber($a, $b, $c, $d);
1007 ini_set('precision', '14');
1008 $this->assertFormatNumber($a, $b, $c, $d);
1009 ini_set('precision', '10');
1010 $this->assertFormatNumber($a, $b, $c, $d);
1011 ini_set('precision', '5');
1012 $this->assertFormatNumber($a, $b, $c, $d);
1013 ini_set('precision', '-1');
1014 $this->assertFormatNumber($a, $b, $c, $d);
1015 } finally {
1016 ini_set('precision', $old_precision);
1019 // Test with different translations
1020 $translator = Loader::getInstance()->getTranslator();
1022 try {
1023 // German
1024 $translator->setTranslation(',', '.');
1025 $translator->setTranslation('.', ',');
1026 $expected = str_replace([',', 'X'], ['.', ','], str_replace('.', 'X', $d));
1027 $this->assertFormatNumber($a, $b, $c, $expected);
1029 // Czech
1030 $translator->setTranslation(',', ' ');
1031 $translator->setTranslation('.', ',');
1032 $expected = str_replace([',', 'X'], [' ', ','], str_replace('.', 'X', $d));
1033 $this->assertFormatNumber($a, $b, $c, $expected);
1034 } finally {
1035 // Restore
1036 $translator->setTranslation(',', ',');
1037 $translator->setTranslation('.', '.');
1042 * format number data provider
1044 * @return array
1046 public function providerFormatNumber(): array
1048 return [
1053 '10 ',
1056 100,
1059 '100 ',
1062 100,
1065 '100 ',
1068 '100',
1071 '100 ',
1074 -1000.454,
1077 '-1,000.45 ',
1080 '-1000.454',
1083 '-1,000.45 ',
1086 0.00003,
1089 '30 µ',
1092 0.003,
1095 '3 m',
1098 -0.003,
1101 '-3,000 µ',
1104 100.98,
1107 '100.98',
1110 21010101,
1113 '21,010,101.00',
1116 1100000000,
1119 '1,100 M',
1122 '1100000000',
1125 '1,100 M',
1128 20000,
1131 '20 k',
1134 20011,
1137 '20.01 k',
1140 123456789,
1143 '123,457 k',
1146 -123456789,
1149 '-123.46 M',
1155 '0',
1161 * Test for Util::getFormattedMaximumUploadSize
1163 * @param int|float $size Size (float on some cpu architectures)
1164 * @param string $unit Unit
1165 * @param string $res Result
1167 * @dataProvider providerGetFormattedMaximumUploadSize
1169 public function testGetFormattedMaximumUploadSize($size, string $unit, string $res): void
1171 $this->assertEquals(
1172 '(' . __('Max: ') . $res . $unit . ')',
1173 Util::getFormattedMaximumUploadSize($size)
1178 * Data provider for testGetFormattedMaximumUploadSize
1180 * @return array
1182 public function providerGetFormattedMaximumUploadSize(): array
1184 return [
1187 __('B'),
1188 '10',
1191 100,
1192 __('B'),
1193 '100',
1196 1024,
1197 __('B'),
1198 '1,024',
1201 102400,
1202 __('KiB'),
1203 '100',
1206 10240000,
1207 __('MiB'),
1208 '10',
1211 2147483648,
1212 __('MiB'),
1213 '2,048',
1216 21474836480,
1217 __('GiB'),
1218 '20',
1221 '153600K',
1222 __('KiB'),
1223 '150',
1226 '157286400',
1227 __('MiB'),
1228 '150',
1231 // Equals to Core::getRealSize of '102400K'
1232 // according to PHP FAQ on "shorthandbytes"
1233 102400 * 1024,
1234 __('MiB'),
1235 '100',
1241 * Test for Util::getTitleForTarget
1243 * @param string $target Target
1244 * @param string $result Expected value
1246 * @dataProvider providerGetTitleForTarget
1248 public function testGetTitleForTarget(string $target, string $result): void
1250 $this->assertEquals(
1251 $result,
1252 Util::getTitleForTarget($target)
1257 * Data provider for testGetTitleForTarget
1259 * @return array
1261 public function providerGetTitleForTarget(): array
1263 return [
1265 'structure',
1266 __('Structure'),
1269 'sql',
1270 __('SQL'),
1273 'search',
1274 __('Search'),
1277 'insert',
1278 __('Insert'),
1281 'browse',
1282 __('Browse'),
1285 'operations',
1286 __('Operations'),
1292 * localised date test, globals are defined
1294 * @param int $a Current timestamp
1295 * @param string $b Format
1296 * @param string $e Expected output
1297 * @param string $tz Timezone to set
1298 * @param string $locale Locale to set
1300 * @dataProvider providerLocalisedDate
1302 public function testLocalisedDate(int $a, string $b, string $e, string $tz, string $locale): void
1304 // A test case for #15830 could be added for using the php setlocale on a Windows CI
1305 // See https://github.com/phpmyadmin/phpmyadmin/issues/15830
1306 _setlocale(LC_ALL, $locale);
1307 $tmpTimezone = date_default_timezone_get();
1308 date_default_timezone_set($tz);
1310 $this->assertEquals(
1312 Util::localisedDate($a, $b)
1315 date_default_timezone_set($tmpTimezone);
1316 _setlocale(LC_ALL, 'en');
1320 * data provider for localised date test
1322 * @return array
1324 public function providerLocalisedDate(): array
1326 $hasJaTranslations = file_exists(LOCALE_PATH . '/cs/LC_MESSAGES/phpmyadmin.mo');
1328 return [
1330 1227455558,
1332 'Nov 23, 2008 at 03:52 PM',
1333 'Europe/London',
1334 'en',
1337 1227455558,
1338 '%Y-%m-%d %H:%M:%S %a',
1339 '2008-11-23 15:52:38 Sun',
1340 'Europe/London',
1341 'en',
1344 1227455558,
1345 '%Y-%m-%d %H:%M:%S %a',
1346 '2008-11-23 16:52:38 Sun',
1347 'Europe/Paris',
1348 'en',
1351 1227455558,
1352 '%Y-%m-%d %H:%M:%S %a',
1353 '2008-11-24 00:52:38 Mon',
1354 'Asia/Tokyo',
1355 'en',
1358 1227455558,
1359 '%a %A %b %B',
1360 'Mon Mon Nov Nov',
1361 'Asia/Tokyo',
1362 'en',
1365 1227455558,
1366 '%a %A %b %B %P',
1367 'Mon Mon Nov Nov AM',
1368 'Asia/Tokyo',
1369 'en',
1372 1227455558,
1373 '%Y-%m-%d %H:%M:%S %a',
1374 $hasJaTranslations ? '2008-11-24 00:52:38 月' : '2008-11-24 00:52:38 Mon',
1375 'Asia/Tokyo',
1376 'ja',
1379 1227455558,
1380 '%a %A %b %B',
1381 $hasJaTranslations ? '月 月 11 月 11 月' : 'Mon Mon Nov Nov',
1382 'Asia/Tokyo',
1383 'ja',
1386 1227455558,
1387 '%a %A %b %B %P',
1388 $hasJaTranslations ? '月 月 11 月 11 月 午前' : 'Mon Mon Nov Nov AM',
1389 'Asia/Tokyo',
1390 'ja',
1393 1227455558,
1394 '月月',
1395 '月月',
1396 'Asia/Tokyo',
1397 'ja',
1400 1227455558,
1401 '%Y 年 2 月 %d %H:%M',
1402 '2008 年 2 月 24 日 00:52',
1403 'Asia/Tokyo',
1404 'ja',
1407 1227455558,
1408 '%Y 年 2 � %d %H:%M',
1409 '2008 年 2 � 24 日 00:52',
1410 'Asia/Tokyo',
1411 'ja',
1414 1617153941,
1415 'H:i:s Y-d-m',
1416 'H:i:s Y-d-m',// Not a valid strftime format
1417 'Europe/Paris',
1418 'fr',
1421 1617153941,
1423 'mer. 31 mars 2021 à 03:25',// No format uses format "%B %d, %Y at %I:%M %p"
1424 'Europe/Paris',
1425 'fr',
1431 * localised timestamp test, globals are defined
1433 * @param int $a Timespan in seconds
1434 * @param string $e Expected output
1436 * @dataProvider providerTimespanFormat
1438 public function testTimespanFormat(int $a, string $e): void
1440 $GLOBALS['timespanfmt'] = '%s days, %s hours, %s minutes and %s seconds';
1441 $tmpTimezone = date_default_timezone_get();
1442 date_default_timezone_set('Europe/London');
1444 $this->assertEquals(
1446 Util::timespanFormat($a)
1449 date_default_timezone_set($tmpTimezone);
1453 * data provider for localised timestamp test
1455 * @return array
1457 public function providerTimespanFormat(): array
1459 return [
1461 1258,
1462 '0 days, 0 hours, 20 minutes and 58 seconds',
1465 821958,
1466 '9 days, 12 hours, 19 minutes and 18 seconds',
1472 * test for generating string contains printable bit value of selected data
1474 * @param int $a Value
1475 * @param int $b Length
1476 * @param string $e Expected output
1478 * @dataProvider providerPrintableBitValue
1480 public function testPrintableBitValue(int $a, int $b, string $e): void
1482 $this->assertEquals(
1484 Util::printableBitValue($a, $b)
1489 * data provider for printable bit value test
1491 * @return array
1493 public function providerPrintableBitValue(): array
1495 return [
1497 20131009,
1499 '0000000000000000000000000000000000000001001100110010110011000001',
1504 '00000000000000000000000000000101',
1510 * PhpMyAdmin\Util::unQuote test
1512 * @param string $param String
1513 * @param string $expected Expected output
1515 * @dataProvider providerUnQuote
1517 public function testUnQuote(string $param, string $expected): void
1519 $this->assertEquals(
1520 $expected,
1521 Util::unQuote($param)
1526 * data provider for PhpMyAdmin\Util::unQuote test
1528 * @return array
1530 public function providerUnQuote(): array
1532 return [
1534 '"test\'"',
1535 "test'",
1538 "'test''",
1539 "test'",
1542 "`test'`",
1543 "test'",
1546 "'test'test",
1547 "'test'test",
1553 * PhpMyAdmin\Util::unQuote test with chosen quote
1555 * @param string $param String
1556 * @param string $expected Expected output
1558 * @dataProvider providerUnQuoteSelectedChar
1560 public function testUnQuoteSelectedChar(string $param, string $expected): void
1562 $this->assertEquals(
1563 $expected,
1564 Util::unQuote($param, '"')
1569 * data provider for PhpMyAdmin\Util::unQuote test with chosen quote
1571 * @return array
1573 public function providerUnQuoteSelectedChar(): array
1575 return [
1577 '"test\'"',
1578 "test'",
1581 "'test''",
1582 "'test''",
1585 "`test'`",
1586 "`test'`",
1589 "'test'test",
1590 "'test'test",
1596 * @dataProvider providerForTestBackquote
1598 public function testBackquote(?string $entry, string $expectedNoneOutput, string $expectedMssqlOutput): void
1600 $this->assertSame($expectedNoneOutput, Util::backquote($entry));
1601 $this->assertEquals($entry, Util::backquoteCompat($entry, 'NONE', false));
1602 $this->assertEquals($entry, Util::backquoteCompat($entry, 'MSSQL', false));
1603 $this->assertSame($expectedNoneOutput, Util::backquoteCompat($entry, 'NONE'));
1604 $this->assertSame($expectedMssqlOutput, Util::backquoteCompat($entry, 'MSSQL'));
1608 * @return array<int|string, string|null>[]
1610 public function providerForTestBackquote(): array
1612 return [
1614 '0',
1615 '`0`',
1616 '"0"',
1619 'test',
1620 '`test`',
1621 '"test"',
1624 'te`st',
1625 '`te``st`',
1626 '"te`st"',
1629 'te"st',
1630 '`te"st`',
1631 '"te\"st"',
1639 '*',
1640 '*',
1641 '*',
1644 null,
1652 * backquoteCompat test with forbidden words
1654 public function testBackquoteForbidenWords(): void
1656 foreach (Context::$KEYWORDS as $keyword => $type) {
1657 if ($type & Token::FLAG_KEYWORD_RESERVED) {
1658 $this->assertEquals(
1659 '`' . $keyword . '`',
1660 Util::backquoteCompat($keyword, 'NONE', false)
1662 } else {
1663 $this->assertEquals(
1664 $keyword,
1665 Util::backquoteCompat($keyword, 'NONE', false)
1672 * test of generating user dir, globals are defined
1674 * @param string $a String
1675 * @param string $e Expected output
1677 * @dataProvider providerUserDir
1679 public function testUserDir(string $a, string $e): void
1681 $GLOBALS['cfg']['Server']['user'] = 'root';
1683 $this->assertEquals($e, Util::userDir($a));
1687 * data provider for PhpMyAdmin\Util::userDir test
1689 * @return array
1691 public function providerUserDir(): array
1693 return [
1695 '/var/pma_tmp/%u/',
1696 '/var/pma_tmp/root/',
1699 '/home/%u/pma',
1700 '/home/root/pma/',
1706 * duplicate first newline test
1708 * @param string $a String
1709 * @param string $e Expected output
1711 * @dataProvider providerDuplicateFirstNewline
1713 public function testDuplicateFirstNewline(string $a, string $e): void
1715 $this->assertEquals(
1717 Util::duplicateFirstNewline($a)
1722 * data provider for duplicate first newline test
1724 * @return array
1726 public function providerDuplicateFirstNewline(): array
1728 return [
1730 'test',
1731 'test',
1734 "\r\ntest",
1735 "\n\r\ntest",
1738 "\ntest",
1739 "\ntest",
1742 "\n\r\ntest",
1743 "\n\r\ntest",
1748 public function testUnsupportedDatatypes(): void
1750 $no_support_types = [];
1751 $this->assertEquals(
1752 $no_support_types,
1753 Util::unsupportedDatatypes()
1757 public function testGetPageFromPosition(): void
1759 $this->assertEquals(Util::getPageFromPosition(0, 1), 1);
1760 $this->assertEquals(Util::getPageFromPosition(1, 1), 2);
1761 $this->assertEquals(Util::getPageFromPosition(1, 2), 1);
1762 $this->assertEquals(Util::getPageFromPosition(1, 6), 1);
1766 * Test for Util::isInteger
1768 * @param bool $expected Expected result for a given input
1769 * @param mixed $input Input data to check
1771 * @dataProvider providerIsInteger
1773 public function testIsInteger(bool $expected, $input): void
1775 $isInteger = Util::isInteger($input);
1776 $this->assertEquals($expected, $isInteger);
1780 * Data provider for Util::isInteger test
1782 * @return array
1784 public function providerIsInteger(): array
1786 return [
1788 true,
1789 1000,
1792 true,
1793 '1000',
1796 false,
1797 1000.1,
1800 false,
1801 '1000.1',
1804 false,
1805 'input',
1811 * Test for Util::getProtoFromForwardedHeader
1813 * @param string $header The http Forwarded header
1814 * @param string $proto The protocol http/https
1816 * @dataProvider providerForwardedHeaders
1818 public function testGetProtoFromForwardedHeader(string $header, string $proto): void
1820 $protocolDetected = Util::getProtoFromForwardedHeader($header);
1821 $this->assertEquals($proto, $protocolDetected);
1825 * Data provider for Util::getProtoFromForwardedHeader test
1827 * @return array
1829 * @source https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded MDN docs
1830 * @source https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/ Nginx docs
1832 public function providerForwardedHeaders(): array
1834 return [
1840 '=',
1844 'https',
1848 'https',
1852 '=https',
1856 '=http',
1860 'For="[2001:db8:cafe::17]:4711"',
1864 'for=192.0.2.60;proto=http;by=203.0.113.43',
1865 'http',
1868 'for=192.0.2.43, for=198.51.100.17',
1872 'for=123.34.567.89',
1876 'for=192.0.2.43, for="[2001:db8:cafe::17]"',
1880 'for=12.34.56.78;host=example.com;proto=https, for=23.45.67.89',
1881 'https',
1884 'for=12.34.56.78, for=23.45.67.89;secret=egah2CGj55fSJFs, for=10.1.2.3',
1888 'for=injected;by="',
1892 'for=injected;by=", for=real',
1896 'for=192.0.2.60;proto=http;by=203.0.113.43',
1897 'http',
1900 'for=192.0.2.60;proto=htTp;by=203.0.113.43',
1901 'http',
1904 'for=192.0.2.60;proto=HTTP;by=203.0.113.43',
1905 'http',
1908 'for=192.0.2.60;proto= http;by=203.0.113.43',
1909 'http',
1912 'for=12.34.45.67;secret="special;proto=abc;test=1";proto=http,for=23.45.67.89',
1913 'http',
1916 'for=12.34.45.67;secret="special;proto=abc;test=1";proto=418,for=23.45.67.89',
1919 /*[ // this test case is very special and would need a different implementation
1920 'for=12.34.45.67;secret="special;proto=http;test=1";proto=https,for=23.45.67.89',
1921 'https'
1926 public function testCurrentUserHasPrivilegeSkipGrantTables(): void
1928 $dbi = $this->getMockBuilder(DatabaseInterface::class)
1929 ->disableOriginalConstructor()
1930 ->getMock();
1931 $dbi->expects($this->once())
1932 ->method('getCurrentUserAndHost')
1933 ->will($this->returnValue(['', '']));
1935 $oldDbi = $GLOBALS['dbi'];
1936 $GLOBALS['dbi'] = $dbi;
1937 $this->assertTrue(Util::currentUserHasPrivilege('EVENT'));
1938 $GLOBALS['dbi'] = $oldDbi;
1941 public function testCurrentUserHasUserPrivilege(): void
1943 $dbi = $this->getMockBuilder(DatabaseInterface::class)
1944 ->disableOriginalConstructor()
1945 ->getMock();
1946 $dbi->expects($this->once())
1947 ->method('getCurrentUserAndHost')
1948 ->will($this->returnValue(['groot_%', '%']));
1949 $dbi->expects($this->once())
1950 ->method('fetchValue')
1951 ->with(
1952 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
1953 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
1955 ->will($this->returnValue('EVENT'));
1957 $oldDbi = $GLOBALS['dbi'];
1958 $GLOBALS['dbi'] = $dbi;
1959 $this->assertTrue(Util::currentUserHasPrivilege('EVENT'));
1960 $GLOBALS['dbi'] = $oldDbi;
1963 public function testCurrentUserHasNotUserPrivilege(): void
1965 $dbi = $this->getMockBuilder(DatabaseInterface::class)
1966 ->disableOriginalConstructor()
1967 ->getMock();
1968 $dbi->expects($this->once())
1969 ->method('getCurrentUserAndHost')
1970 ->will($this->returnValue(['groot_%', '%']));
1971 $dbi->expects($this->once())
1972 ->method('fetchValue')
1973 ->with(
1974 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
1975 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
1977 ->will($this->returnValue(false));
1979 $oldDbi = $GLOBALS['dbi'];
1980 $GLOBALS['dbi'] = $dbi;
1981 $this->assertFalse(Util::currentUserHasPrivilege('EVENT'));
1982 $GLOBALS['dbi'] = $oldDbi;
1985 public function testCurrentUserHasNotUserPrivilegeButDbPrivilege(): void
1987 $dbi = $this->getMockBuilder(DatabaseInterface::class)
1988 ->onlyMethods(['getCurrentUserAndHost', 'fetchValue'])
1989 ->disableOriginalConstructor()
1990 ->getMock();
1992 $dbi->expects($this->once())
1993 ->method('getCurrentUserAndHost')
1994 ->will($this->returnValue(['groot_%', '%']));
1995 $dbi->expects($this->exactly(2))
1996 ->method('fetchValue')
1997 ->withConsecutive(
1999 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
2000 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'",
2003 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`'
2004 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
2005 . " AND 'my_data_base' LIKE `TABLE_SCHEMA`",
2008 ->willReturnOnConsecutiveCalls(false, 'EVENT');
2010 $oldDbi = $GLOBALS['dbi'];
2011 $GLOBALS['dbi'] = $dbi;
2012 $this->assertTrue(Util::currentUserHasPrivilege('EVENT', 'my_data_base'));
2013 $GLOBALS['dbi'] = $oldDbi;
2016 public function testCurrentUserHasNotUserPrivilegeAndNotDbPrivilege(): void
2018 $dbi = $this->getMockBuilder(DatabaseInterface::class)
2019 ->onlyMethods(['getCurrentUserAndHost', 'fetchValue'])
2020 ->disableOriginalConstructor()
2021 ->getMock();
2023 $dbi->expects($this->once())
2024 ->method('getCurrentUserAndHost')
2025 ->will($this->returnValue(['groot_%', '%']));
2026 $dbi->expects($this->exactly(2))
2027 ->method('fetchValue')
2028 ->withConsecutive(
2030 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
2031 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'",
2034 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`'
2035 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
2036 . " AND 'my_data_base' LIKE `TABLE_SCHEMA`",
2039 ->willReturnOnConsecutiveCalls(false, false);
2041 $oldDbi = $GLOBALS['dbi'];
2042 $GLOBALS['dbi'] = $dbi;
2043 $this->assertFalse(Util::currentUserHasPrivilege('EVENT', 'my_data_base'));
2044 $GLOBALS['dbi'] = $oldDbi;
2047 public function testCurrentUserHasNotUserPrivilegeAndNotDbPrivilegeButTablePrivilege(): void
2049 $dbi = $this->getMockBuilder(DatabaseInterface::class)
2050 ->onlyMethods(['getCurrentUserAndHost', 'fetchValue'])
2051 ->disableOriginalConstructor()
2052 ->getMock();
2054 $dbi->expects($this->once())
2055 ->method('getCurrentUserAndHost')
2056 ->will($this->returnValue(['groot_%', '%']));
2057 $dbi->expects($this->exactly(3))
2058 ->method('fetchValue')
2059 ->withConsecutive(
2061 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
2062 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'",
2065 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`'
2066 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
2067 . " AND 'my_data_base' LIKE `TABLE_SCHEMA`",
2070 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`TABLE_PRIVILEGES`'
2071 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
2072 . " AND 'my_data_base' LIKE `TABLE_SCHEMA` AND TABLE_NAME='my_data_table'",
2075 ->willReturnOnConsecutiveCalls(false, false, 'EVENT');
2077 $oldDbi = $GLOBALS['dbi'];
2078 $GLOBALS['dbi'] = $dbi;
2079 $this->assertTrue(Util::currentUserHasPrivilege('EVENT', 'my_data_base', 'my_data_table'));
2080 $GLOBALS['dbi'] = $oldDbi;
2083 public function testCurrentUserHasNotUserPrivilegeAndNotDbPrivilegeAndNotTablePrivilege(): void
2085 $dbi = $this->getMockBuilder(DatabaseInterface::class)
2086 ->onlyMethods(['getCurrentUserAndHost', 'fetchValue'])
2087 ->disableOriginalConstructor()
2088 ->getMock();
2090 $dbi->expects($this->once())
2091 ->method('getCurrentUserAndHost')
2092 ->will($this->returnValue(['groot_%', '%']));
2093 $dbi->expects($this->exactly(3))
2094 ->method('fetchValue')
2095 ->withConsecutive(
2097 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES`'
2098 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'",
2101 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES`'
2102 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
2103 . " AND 'my_data_base' LIKE `TABLE_SCHEMA`",
2106 'SELECT `PRIVILEGE_TYPE` FROM `INFORMATION_SCHEMA`.`TABLE_PRIVILEGES`'
2107 . " WHERE GRANTEE='''groot_%''@''%''' AND PRIVILEGE_TYPE='EVENT'"
2108 . " AND 'my_data_base' LIKE `TABLE_SCHEMA` AND TABLE_NAME='my_data_table'",
2111 ->willReturnOnConsecutiveCalls(false, false, false);
2113 $oldDbi = $GLOBALS['dbi'];
2114 $GLOBALS['dbi'] = $dbi;
2115 $this->assertFalse(Util::currentUserHasPrivilege('EVENT', 'my_data_base', 'my_data_table'));
2116 $GLOBALS['dbi'] = $oldDbi;
2120 * @return array[]
2122 public function dataProviderScriptNames(): array
2124 // target
2125 // location
2126 // function output
2127 return [
2129 'structure', // Notice the typo on db_structure.php
2130 'databasesss',
2131 'index.php?route=/&lang=en', // Fallback to the base route
2134 'db_structures.php', // Notice the typo on databases
2135 'database',
2136 'index.php?route=/&lang=en', // Fallback to the base route
2139 'tbl_structure.php', // Support the legacy value
2140 'table',
2141 'index.php?route=/table/structure&lang=en',
2144 'structure',
2145 'table',
2146 'index.php?route=/table/structure&lang=en',
2149 'tbl_sql.php', // Support the legacy value
2150 'table',
2151 'index.php?route=/table/sql&lang=en',
2154 'sql',
2155 'table',
2156 'index.php?route=/table/sql&lang=en',
2159 'tbl_select.php', // Support the legacy value
2160 'table',
2161 'index.php?route=/table/search&lang=en',
2164 'search',
2165 'table',
2166 'index.php?route=/table/search&lang=en',
2169 'tbl_change.php', // Support the legacy value
2170 'table',
2171 'index.php?route=/table/change&lang=en',
2174 'insert',
2175 'table',
2176 'index.php?route=/table/change&lang=en',
2179 'sql.php', // Support the legacy value
2180 'table',
2181 'index.php?route=/sql&lang=en',
2184 'browse',
2185 'table',
2186 'index.php?route=/sql&lang=en',
2189 'db_structure.php', // Support the legacy value
2190 'database',
2191 'index.php?route=/database/structure&lang=en',
2194 'structure',
2195 'database',
2196 'index.php?route=/database/structure&lang=en',
2199 'db_sql.php', // Support the legacy value
2200 'database',
2201 'index.php?route=/database/sql&lang=en',
2204 'sql',
2205 'database',
2206 'index.php?route=/database/sql&lang=en',
2209 'db_search.php', // Support the legacy value
2210 'database',
2211 'index.php?route=/database/search&lang=en',
2214 'search',
2215 'database',
2216 'index.php?route=/database/search&lang=en',
2219 'db_operations.php', // Support the legacy value
2220 'database',
2221 'index.php?route=/database/operations&lang=en',
2224 'operations',
2225 'database',
2226 'index.php?route=/database/operations&lang=en',
2229 'index.php', // Support the legacy value
2230 'server',
2231 'index.php?route=/&lang=en',
2234 'welcome',
2235 'server',
2236 'index.php?route=/&lang=en',
2239 'server_databases.php', // Support the legacy value
2240 'server',
2241 'index.php?route=/server/databases&lang=en',
2244 'databases',
2245 'server',
2246 'index.php?route=/server/databases&lang=en',
2249 'server_status.php', // Support the legacy value
2250 'server',
2251 'index.php?route=/server/status&lang=en',
2254 'status',
2255 'server',
2256 'index.php?route=/server/status&lang=en',
2259 'server_variables.php', // Support the legacy value
2260 'server',
2261 'index.php?route=/server/variables&lang=en',
2264 'variables',
2265 'server',
2266 'index.php?route=/server/variables&lang=en',
2269 'server_privileges.php', // Support the legacy value
2270 'server',
2271 'index.php?route=/server/privileges&lang=en',
2274 'privileges',
2275 'server',
2276 'index.php?route=/server/privileges&lang=en',
2282 * @dataProvider dataProviderScriptNames
2284 public function testGetScriptNameForOption(string $target, string $location, string $finalLink): void
2286 $this->assertSame(
2287 $finalLink,
2288 Util::getScriptNameForOption($target, $location)