Issues (3069)

src/Config/ConfigFile.php (5 issues)

1
<?php
2
/**
3
 * Config file management
4
 */
5
6
declare(strict_types=1);
7
8
namespace PhpMyAdmin\Config;
9
10
use PhpMyAdmin\Config;
11
use PhpMyAdmin\Core;
12
use PhpMyAdmin\Current;
13
14
use function __;
15
use function _pgettext;
16
use function array_diff;
17
use function array_flip;
18
use function array_keys;
19
use function array_merge;
20
use function count;
21
use function is_array;
22
use function preg_replace;
23
24
/**
25
 * Config file management class.
26
 * Stores its data in $_SESSION
27
 */
28
class ConfigFile
29
{
30
    /**
31
     * Stores default phpMyAdmin config
32
     *
33
     * @see Settings
34
     *
35
     * @var mixed[]
36
     */
37
    private array $defaultCfg;
38
39
    /**
40
     * Stores allowed values for non-standard fields
41
     *
42
     * @var array<string, string|mixed[]>
43
     */
44
    private array $cfgDb;
45
46
    /**
47
     * Whether we are currently working in PMA Setup context
48
     */
49
    private bool $isInSetup;
50
51
    /**
52
     * Keys which will be always written to config file
53
     *
54
     * @var int[]
55
     */
56
    private array $persistKeys = [];
57
58
    /**
59
     * Changes keys while updating config in {@link updateWithGlobalConfig()}
60
     * or reading by {@link getConfig()} or {@link getConfigArray()}
61
     *
62
     * @var mixed[]
63
     */
64
    private array $cfgUpdateReadMapping = [];
65
66
    /**
67
     * Key filter for {@link set()}
68
     *
69
     * @var array-key[]|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment array-key[]|null at position 0 could not be parsed: Unknown type name 'array-key' at position 0 in array-key[]|null.
Loading history...
70
     */
71
    private array|null $setFilter = null;
72
73
    /**
74
     * Instance id (key in $_SESSION array, separate for each server -
75
     * ConfigFile{server id})
76
     */
77
    private string $id;
78
79
    /**
80
     * @param mixed[]|null $baseConfig base configuration read from
81
                             {@link PhpMyAdmin\Config::$base_config},
82
                             use only when not in PMA Setup
83
                             Stores original PMA config, not modified by user preferences
84
     */
85 72
    public function __construct(private array|null $baseConfig = null)
86
    {
87
        // load default config values
88 72
        $settings = new Settings([]);
89 72
        $this->defaultCfg = $settings->asArray();
90
91
        // load additional config information
92 72
        $this->cfgDb = $this->getAllowedValues();
93 72
        $this->isInSetup = $baseConfig === null;
94 72
        $this->id = 'ConfigFile' . Current::$server;
95 72
        if (isset($_SESSION[$this->id])) {
96 4
            return;
97
        }
98
99 72
        $_SESSION[$this->id] = [];
100
    }
101
102
    /**
103
     * Sets names of config options which will be placed in config file even if
104
     * they are set to their default values (use only full paths)
105
     *
106
     * @param list<array-key> $keys the names of the config options
0 ignored issues
show
The type PhpMyAdmin\Config\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...
107
     */
108 8
    public function setPersistKeys(array $keys): void
109
    {
110
        // checking key presence is much faster than searching so move values
111
        // to keys
112 8
        $this->persistKeys = array_flip($keys);
113
    }
114
115
    /**
116
     * Returns flipped array set by {@link setPersistKeys()}
117
     *
118
     * @return int[]
119
     */
120
    public function getPersistKeysMap(): array
121
    {
122
        return $this->persistKeys;
123
    }
124
125
    /**
126
     * By default ConfigFile allows setting of all configuration keys, use
127
     * this method to set up a filter on {@link set()} method
128
     *
129
     * @param string[]|null $keys array of allowed keys or null to remove filter
130
     */
131 4
    public function setAllowedKeys(array|null $keys): void
132
    {
133 4
        if ($keys === null) {
0 ignored issues
show
The condition $keys === null is always false.
Loading history...
134 4
            $this->setFilter = null;
135
136 4
            return;
137
        }
138
139
        // checking key presence is much faster than searching so move values
140
        // to keys
141 4
        $this->setFilter = array_flip($keys);
142
    }
143
144
    /**
145
     * Sets path mapping for updating config in
146
     * {@link updateWithGlobalConfig()} or reading
147
     * by {@link getConfig()} or {@link getConfigArray()}
148
     *
149
     * @param mixed[] $mapping Contains the mapping of "Server/config options"
150
     *                       to "Server/1/config options"
151
     */
152 4
    public function setCfgUpdateReadMapping(array $mapping): void
153
    {
154 4
        $this->cfgUpdateReadMapping = $mapping;
155
    }
156
157
    /**
158
     * Resets configuration data
159
     */
160 8
    public function resetConfigData(): void
161
    {
162 8
        $_SESSION[$this->id] = [];
163
    }
164
165
    /**
166
     * Sets configuration data (overrides old data)
167
     *
168
     * @param mixed[] $cfg Configuration options
169
     */
170 72
    public function setConfigData(array $cfg): void
171
    {
172 72
        $_SESSION[$this->id] = $cfg;
173
    }
174
175
    /**
176
     * Sets config value
177
     */
178 56
    public function set(string $path, mixed $value, string|null $canonicalPath = null): void
179
    {
180 56
        if ($canonicalPath === null) {
181 52
            $canonicalPath = $this->getCanonicalPath($path);
182
        }
183
184 56
        if ($this->setFilter !== null && ! isset($this->setFilter[$canonicalPath])) {
185 4
            return;
186
        }
187
188
        // if the path isn't protected it may be removed
189 56
        if (isset($this->persistKeys[$canonicalPath])) {
190 4
            Core::arrayWrite($path, $_SESSION[$this->id], $value);
191
192 4
            return;
193
        }
194
195 56
        $defaultValue = $this->getDefault($canonicalPath);
196 56
        if ($this->isInSetup) {
197
            // remove if it has a default value or is empty
198 52
            $removePath = $value === $defaultValue || empty($value) && empty($defaultValue);
199
        } else {
200
            // get original config values not overwritten by user
201
            // preferences to allow for overwriting options set in
202
            // config.inc.php with default values
203 4
            $instanceDefaultValue = Core::arrayRead($canonicalPath, $this->baseConfig);
0 ignored issues
show
It seems like $this->baseConfig can also be of type null; however, parameter $array of PhpMyAdmin\Core::arrayRead() does only seem to accept array, 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

203
            $instanceDefaultValue = Core::arrayRead($canonicalPath, /** @scrutinizer ignore-type */ $this->baseConfig);
Loading history...
204
            // remove if it has a default value and base config (config.inc.php)
205
            // uses default value
206 4
            $removePath = $value === $defaultValue && $instanceDefaultValue === $defaultValue;
207
        }
208
209 56
        if ($removePath) {
210 20
            Core::arrayRemove($path, $_SESSION[$this->id]);
211
212 20
            return;
213
        }
214
215 48
        Core::arrayWrite($path, $_SESSION[$this->id], $value);
216
    }
217
218
    /**
219
     * Flattens multidimensional array, changes indices to paths
220
     * (eg. 'key/subkey').
221
     *
222
     * @param mixed[] $array  Multidimensional array
223
     * @param string  $prefix Prefix
224
     *
225
     * @return mixed[]
226
     */
227 36
    private function getFlatArray(array $array, string $prefix = ''): array
228
    {
229 36
        $result = [];
230 36
        foreach ($array as $key => $value) {
231 28
            if (is_array($value) && ! isset($value[0])) {
232 16
                $result += $this->getFlatArray($value, $prefix . $key . '/');
233
            } else {
234 28
                $result[$prefix . $key] = $value;
235
            }
236
        }
237
238 36
        return $result;
239
    }
240
241
    /**
242
     * Returns default config in a flattened array
243
     *
244
     * @return mixed[]
245
     */
246 4
    public function getFlatDefaultConfig(): array
247
    {
248 4
        return $this->getFlatArray($this->defaultCfg);
249
    }
250
251
    /**
252
     * Updates config with values read from given array
253
     * (config will contain differences to defaults from {@see \PhpMyAdmin\Config\Settings}).
254
     *
255
     * @param mixed[] $cfg Configuration
256
     */
257 12
    public function updateWithGlobalConfig(array $cfg): void
258
    {
259
        // load config array and flatten it
260 12
        $flatConfig = $this->getFlatArray($cfg);
261
262
        // save values map for translating a few user preferences paths,
263
        // should be complemented by code reading from generated config
264
        // to perform inverse mapping
265 12
        foreach ($flatConfig as $path => $value) {
266 12
            if (isset($this->cfgUpdateReadMapping[$path])) {
267 4
                $path = $this->cfgUpdateReadMapping[$path];
268
            }
269
270 12
            $this->set($path, $value, $path);
271
        }
272
    }
273
274
    /**
275
     * Returns config value or $default if it's not set
276
     *
277
     * @param string $path    Path of config file
278
     * @param mixed  $default Default values
279
     */
280 16
    public function get(string $path, mixed $default = null): mixed
281
    {
282 16
        return Core::arrayRead($path, $_SESSION[$this->id], $default);
283
    }
284
285
    /**
286
     * Returns default config value or $default it it's not set ie. it doesn't
287
     * exist in {@see \PhpMyAdmin\Config\Settings} ($cfg).
288
     *
289
     * @param string $canonicalPath Canonical path
290
     * @param mixed  $default       Default value
291
     */
292 64
    public function getDefault(string $canonicalPath, mixed $default = null): mixed
293
    {
294 64
        return Core::arrayRead($canonicalPath, $this->defaultCfg, $default);
295
    }
296
297
    /**
298
     * Returns config value, if it's not set uses the default one; returns
299
     * $default if the path isn't set and doesn't contain a default value
300
     *
301
     * @param string $path    Path
302
     * @param mixed  $default Default value
303
     */
304 4
    public function getValue(string $path, mixed $default = null): mixed
305
    {
306 4
        $v = Core::arrayRead($path, $_SESSION[$this->id]);
307 4
        if ($v !== null) {
308 4
            return $v;
309
        }
310
311 4
        $path = $this->getCanonicalPath($path);
312
313 4
        return $this->getDefault($path, $default);
314
    }
315
316
    /**
317
     * Returns canonical path
318
     *
319
     * @param string $path Path
320
     */
321 60
    public function getCanonicalPath(string $path): string
322
    {
323 60
        return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path);
324
    }
325
326
    /**
327
     * Returns config database entry for $path
328
     *
329
     * @param string $path    path of the variable in config db
330
     * @param mixed  $default default value
331
     */
332 4
    public function getDbEntry(string $path, mixed $default = null): mixed
333
    {
334 4
        return Core::arrayRead($path, $this->cfgDb, $default);
335
    }
336
337
    /**
338
     * Returns server count
339
     */
340 4
    public function getServerCount(): int
341
    {
342 4
        return isset($_SESSION[$this->id]['Servers'])
343 4
            ? count($_SESSION[$this->id]['Servers'])
344 4
            : 0;
345
    }
346
347
    /**
348
     * Returns server list
349
     *
350
     * @return mixed[]
351
     */
352 4
    public function getServers(): array
353
    {
354 4
        return $_SESSION[$this->id]['Servers'] ?? [];
355
    }
356
357
    /**
358
     * Returns DSN of given server
359
     *
360
     * @param int $server server index
361
     */
362 4
    public function getServerDSN(int $server): string
363
    {
364 4
        if (! isset($_SESSION[$this->id]['Servers'][$server])) {
365 4
            return '';
366
        }
367
368 4
        $path = 'Servers/' . $server;
369 4
        $dsn = 'mysqli://';
370 4
        if ($this->getValue($path . '/auth_type') === 'config') {
371 4
            $dsn .= $this->getValue($path . '/user');
372 4
            if (! empty($this->getValue($path . '/password'))) {
373 4
                $dsn .= ':***';
374
            }
375
376 4
            $dsn .= '@';
377
        }
378
379 4
        if ($this->getValue($path . '/host') !== 'localhost') {
380 4
            $dsn .= $this->getValue($path . '/host');
381 4
            $port = $this->getValue($path . '/port');
382 4
            if ($port) {
383 4
                $dsn .= ':' . $port;
384
            }
385
        } else {
386 4
            $dsn .= $this->getValue($path . '/socket');
387
        }
388
389 4
        return $dsn;
390
    }
391
392
    /**
393
     * Returns server name
394
     *
395
     * @param int $id server index
396
     */
397 4
    public function getServerName(int $id): string
398
    {
399 4
        if (! isset($_SESSION[$this->id]['Servers'][$id])) {
400 4
            return '';
401
        }
402
403 4
        $verbose = $this->get('Servers/' . $id . '/verbose');
404 4
        if (! empty($verbose)) {
405 4
            return $verbose;
406
        }
407
408 4
        $host = $this->get('Servers/' . $id . '/host');
409
410 4
        return empty($host) ? 'localhost' : $host;
411
    }
412
413
    /**
414
     * Removes server
415
     *
416
     * @param int $server server index
417
     */
418 4
    public function removeServer(int $server): void
419
    {
420 4
        if (! isset($_SESSION[$this->id]['Servers'][$server])) {
421
            return;
422
        }
423
424 4
        $lastServer = $this->getServerCount();
425
426
        /** @infection-ignore-all */
427 4
        for ($i = $server; $i < $lastServer; $i++) {
428 4
            $_SESSION[$this->id]['Servers'][$i] = $_SESSION[$this->id]['Servers'][$i + 1];
429
        }
430
431 4
        unset($_SESSION[$this->id]['Servers'][$lastServer]);
432
433 4
        if (! isset($_SESSION[$this->id]['ServerDefault']) || $_SESSION[$this->id]['ServerDefault'] != $lastServer) {
434 4
            return;
435
        }
436
437 4
        unset($_SESSION[$this->id]['ServerDefault']);
438
    }
439
440
    /**
441
     * Returns configuration array (full, multidimensional format)
442
     *
443
     * @return mixed[]
444
     */
445 40
    public function getConfig(): array
446
    {
447 40
        $c = $_SESSION[$this->id];
448 40
        foreach ($this->cfgUpdateReadMapping as $mapTo => $mapFrom) {
449
            // if the key $c exists in $map_to
450 4
            if (Core::arrayRead($mapTo, $c) === null) {
451 4
                continue;
452
            }
453
454
            Core::arrayWrite($mapTo, $c, Core::arrayRead($mapFrom, $c));
455
            Core::arrayRemove($mapFrom, $c);
456
        }
457
458 40
        return $c;
459
    }
460
461
    /**
462
     * Returns configuration array (flat format)
463
     *
464
     * @return mixed[]
465
     */
466 20
    public function getConfigArray(): array
467
    {
468 20
        $c = $this->getFlatArray($_SESSION[$this->id]);
469
470 20
        $persistKeys = array_diff(
471 20
            array_keys($this->persistKeys),
472 20
            array_keys($c),
473 20
        );
474 20
        foreach ($persistKeys as $k) {
475 8
            $c[$k] = $this->getDefault($this->getCanonicalPath($k));
476
        }
477
478 20
        foreach ($this->cfgUpdateReadMapping as $mapTo => $mapFrom) {
479
            if (! isset($c[$mapFrom])) {
480
                continue;
481
            }
482
483
            $c[$mapTo] = $c[$mapFrom];
484
            unset($c[$mapFrom]);
485
        }
486
487 20
        return $c;
488
    }
489
490
    /**
491
     * Database with allowed values for configuration stored in the $cfg array,
492
     * used by setup script and user preferences to generate forms.
493
     *
494
     * Value meaning:
495
     *   array - select field, array contains allowed values
496
     *   string - type override
497
     *
498
     * @return array<string, string|mixed[]>
499
     */
500 72
    public function getAllowedValues(): array
501
    {
502 72
        $config = Config::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::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

502
        $config = /** @scrutinizer ignore-deprecated */ Config::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...
503
504 72
        return [
505 72
            'Servers' => [
506 72
                1 => [
507 72
                    'port' => 'integer',
508 72
                    'auth_type' => ['config', 'http', 'signon', 'cookie'],
509 72
                    'AllowDeny' => ['order' => ['', 'deny,allow', 'allow,deny', 'explicit']],
510 72
                    'only_db' => 'array',
511 72
                ],
512 72
            ],
513 72
            'RecodingEngine' => ['auto', 'iconv', 'mb', 'none'],
514 72
            'MemoryLimit' => 'short_string',
515 72
            'NavigationLogoLinkWindow' => ['main', 'new'],
516 72
            'NavigationTreeDefaultTabTable' => [
517
                // fields list
518 72
                '/table/structure' => __('Structure'),
519
                // SQL form
520 72
                '/table/sql' => __('SQL'),
521
                // search page
522 72
                '/table/search' => __('Search'),
523
                // insert row page
524 72
                '/table/change' => __('Insert'),
525
                // browse page
526 72
                '/sql' => __('Browse'),
527 72
            ],
528 72
            'NavigationTreeDefaultTabTable2' => [
529
                //don't display
530 72
                '' => '',
531
                // fields list
532 72
                '/table/structure' => __('Structure'),
533
                // SQL form
534 72
                '/table/sql' => __('SQL'),
535
                // search page
536 72
                '/table/search' => __('Search'),
537
                // insert row page
538 72
                '/table/change' => __('Insert'),
539
                // browse page
540 72
                '/sql' => __('Browse'),
541 72
            ],
542 72
            'NavigationTreeDbSeparator' => 'short_string',
543 72
            'NavigationTreeTableSeparator' => 'short_string',
544 72
            'NavigationWidth' => 'integer',
545 72
            'TableNavigationLinksMode' => ['icons' => __('Icons'), 'text' => __('Text'), 'both' => __('Both')],
546 72
            'MaxRows' => [25, 50, 100, 250, 500],
547 72
            'Order' => ['ASC', 'DESC', 'SMART'],
548 72
            'RowActionLinks' => [
549 72
                'none' => __('Nowhere'),
550 72
                'left' => __('Left'),
551 72
                'right' => __('Right'),
552 72
                'both' => __('Both'),
553 72
            ],
554 72
            'TablePrimaryKeyOrder' => ['NONE' => __('None'), 'ASC' => __('Ascending'), 'DESC' => __('Descending')],
555 72
            'ProtectBinary' => [false, 'blob', 'noblob', 'all'],
556 72
            'CharEditing' => ['input', 'textarea'],
557 72
            'TabsMode' => ['icons' => __('Icons'), 'text' => __('Text'), 'both' => __('Both')],
558 72
            'PDFDefaultPageSize' => [
559 72
                'A3' => 'A3',
560 72
                'A4' => 'A4',
561 72
                'A5' => 'A5',
562 72
                'letter' => 'letter',
563 72
                'legal' => 'legal',
564 72
            ],
565 72
            'ActionLinksMode' => ['icons' => __('Icons'), 'text' => __('Text'), 'both' => __('Both')],
566 72
            'GridEditing' => [
567 72
                'click' => __('Click'),
568 72
                'double-click' => __('Double click'),
569 72
                'disabled' => __('Disabled'),
570 72
            ],
571 72
            'RelationalDisplay' => ['K' => __('key'), 'D' => __('display column')],
572 72
            'DefaultTabServer' => [
573
                // the welcome page (recommended for multiuser setups)
574 72
                '/' => __('Welcome'),
575
                // list of databases
576 72
                '/server/databases' => __('Databases'),
577
                // runtime information
578 72
                '/server/status' => __('Status'),
579
                // MySQL server variables
580 72
                '/server/variables' => __('Variables'),
581
                // user management
582 72
                '/server/privileges' => __('Privileges'),
583 72
            ],
584 72
            'DefaultTabDatabase' => [
585
                // tables list
586 72
                '/database/structure' => __('Structure'),
587
                // SQL form
588 72
                '/database/sql' => __('SQL'),
589
                // search query
590 72
                '/database/search' => __('Search'),
591
                // operations on database
592 72
                '/database/operations' => __('Operations'),
593 72
            ],
594 72
            'DefaultTabTable' => [
595
                // fields list
596 72
                '/table/structure' => __('Structure'),
597
                // SQL form
598 72
                '/table/sql' => __('SQL'),
599
                // search page
600 72
                '/table/search' => __('Search'),
601
                // insert row page
602 72
                '/table/change' => __('Insert'),
603
                // browse page
604 72
                '/sql' => __('Browse'),
605 72
            ],
606 72
            'InitialSlidersState' => ['open' => __('Open'), 'closed' => __('Closed'), 'disabled' => __('Disabled')],
607 72
            'FirstDayOfCalendar' => [
608 72
                1 => _pgettext('Week day name', 'Monday'),
609 72
                2 => _pgettext('Week day name', 'Tuesday'),
610 72
                3 => _pgettext('Week day name', 'Wednesday'),
611 72
                4 => _pgettext('Week day name', 'Thursday'),
612 72
                5 => _pgettext('Week day name', 'Friday'),
613 72
                6 => _pgettext('Week day name', 'Saturday'),
614 72
                7 => _pgettext('Week day name', 'Sunday'),
615 72
            ],
616 72
            'SendErrorReports' => [
617 72
                'ask' => __('Ask before sending error reports'),
618 72
                'always' => __('Always send error reports'),
619 72
                'never' => __('Never send error reports'),
620 72
            ],
621 72
            'DefaultForeignKeyChecks' => [
622 72
                'default' => __('Server default'),
623 72
                'enable' => __('Enable'),
624 72
                'disable' => __('Disable'),
625 72
            ],
626
627 72
            'Import' => [
628 72
                'format' => [
629
                    // CSV
630 72
                    'csv',
631
                    // DocSQL
632 72
                    'docsql',
633
                    // CSV using LOAD DATA
634 72
                    'ldi',
635
                    // SQL
636 72
                    'sql',
637 72
                ],
638 72
                'charset' => array_merge([''], $config->settings['AvailableCharsets'] ?? []),
639 72
                'sql_compatibility' => [
640 72
                    'NONE',
641 72
                    'ANSI',
642 72
                    'DB2',
643 72
                    'MAXDB',
644 72
                    'MYSQL323',
645 72
                    'MYSQL40',
646 72
                    'MSSQL',
647 72
                    'ORACLE',
648
                    // removed; in MySQL 5.0.33, this produces exports that
649
                    // can't be read by POSTGRESQL (see our bug #1596328)
650
                    //'POSTGRESQL',
651 72
                    'TRADITIONAL',
652 72
                ],
653 72
                'csv_terminated' => 'short_string',
654 72
                'csv_enclosed' => 'short_string',
655 72
                'csv_escaped' => 'short_string',
656 72
                'ldi_terminated' => 'short_string',
657 72
                'ldi_enclosed' => 'short_string',
658 72
                'ldi_escaped' => 'short_string',
659 72
                'ldi_local_option' => ['auto', true, false],
660 72
            ],
661
662 72
            'Export' => [
663 72
                '_sod_select' => [
664 72
                    'structure' => __('structure'),
665 72
                    'data' => __('data'),
666 72
                    'structure_and_data' => __('structure and data'),
667 72
                ],
668 72
                'method' => [
669 72
                    'quick' => __('Quick - display only the minimal options to configure'),
670 72
                    'custom' => __('Custom - display all possible options to configure'),
671 72
                    'custom-no-form' => __('Custom - like above, but without the quick/custom choice'),
672 72
                ],
673 72
                'format' => [
674 72
                    'codegen',
675 72
                    'csv',
676 72
                    'excel',
677 72
                    'htmlexcel',
678 72
                    'htmlword',
679 72
                    'latex',
680 72
                    'ods',
681 72
                    'odt',
682 72
                    'pdf',
683 72
                    'sql',
684 72
                    'texytext',
685 72
                    'xml',
686 72
                    'yaml',
687 72
                ],
688 72
                'compression' => ['none', 'zip', 'gzip'],
689 72
                'charset' => array_merge([''], $config->settings['AvailableCharsets'] ?? []),
690 72
                'sql_compatibility' => [
691 72
                    'NONE',
692 72
                    'ANSI',
693 72
                    'DB2',
694 72
                    'MAXDB',
695 72
                    'MYSQL323',
696 72
                    'MYSQL40',
697 72
                    'MSSQL',
698 72
                    'ORACLE',
699
                    // removed; in MySQL 5.0.33, this produces exports that
700
                    // can't be read by POSTGRESQL (see our bug #1596328)
701
                    //'POSTGRESQL',
702 72
                    'TRADITIONAL',
703 72
                ],
704 72
                'codegen_format' => ['#', 'NHibernate C# DO', 'NHibernate XML'],
705 72
                'csv_separator' => 'short_string',
706 72
                'csv_terminated' => 'short_string',
707 72
                'csv_enclosed' => 'short_string',
708 72
                'csv_escaped' => 'short_string',
709 72
                'csv_null' => 'short_string',
710 72
                'excel_null' => 'short_string',
711 72
                'excel_edition' => [
712 72
                    'win' => 'Windows',
713 72
                    'mac_excel2003' => 'Excel 2003 / Macintosh',
714 72
                    'mac_excel2008' => 'Excel 2008 / Macintosh',
715 72
                ],
716 72
                'sql_structure_or_data' => [
717 72
                    'structure' => __('structure'),
718 72
                    'data' => __('data'),
719 72
                    'structure_and_data' => __('structure and data'),
720 72
                ],
721 72
                'sql_type' => ['INSERT', 'UPDATE', 'REPLACE'],
722 72
                'sql_insert_syntax' => [
723 72
                    'complete' => __('complete inserts'),
724 72
                    'extended' => __('extended inserts'),
725 72
                    'both' => __('both of the above'),
726 72
                    'none' => __('neither of the above'),
727 72
                ],
728 72
                'htmlword_structure_or_data' => [
729 72
                    'structure' => __('structure'),
730 72
                    'data' => __('data'),
731 72
                    'structure_and_data' => __('structure and data'),
732 72
                ],
733 72
                'htmlword_null' => 'short_string',
734 72
                'ods_null' => 'short_string',
735 72
                'odt_null' => 'short_string',
736 72
                'odt_structure_or_data' => [
737 72
                    'structure' => __('structure'),
738 72
                    'data' => __('data'),
739 72
                    'structure_and_data' => __('structure and data'),
740 72
                ],
741 72
                'texytext_structure_or_data' => [
742 72
                    'structure' => __('structure'),
743 72
                    'data' => __('data'),
744 72
                    'structure_and_data' => __('structure and data'),
745 72
                ],
746 72
                'texytext_null' => 'short_string',
747 72
            ],
748
749 72
            'Console' => [
750 72
                'Mode' => ['info', 'show', 'collapse'],
751 72
                'OrderBy' => ['exec', 'time', 'count'],
752 72
                'Order' => ['asc', 'desc'],
753 72
            ],
754
755
            /**
756
             * Basic validator assignments (functions from libraries/config/Validator.php
757
             * and 'window.validators' object in js/config.js)
758
             * Use only full paths and form ids
759
             */
760 72
            '_validators' => [
761 72
                'Console/Height' => 'validateNonNegativeNumber',
762 72
                'CharTextareaCols' => 'validatePositiveNumber',
763 72
                'CharTextareaRows' => 'validatePositiveNumber',
764 72
                'ExecTimeLimit' => 'validateNonNegativeNumber',
765 72
                'Export/sql_max_query_size' => 'validatePositiveNumber',
766 72
                'FirstLevelNavigationItems' => 'validatePositiveNumber',
767 72
                'ForeignKeyMaxLimit' => 'validatePositiveNumber',
768 72
                'Import/csv_enclosed' => [['validateByRegex', '/^.?$/']],
769 72
                'Import/csv_escaped' => [['validateByRegex', '/^.$/']],
770 72
                'Import/csv_terminated' => [['validateByRegex', '/^.$/']],
771 72
                'Import/ldi_enclosed' => [['validateByRegex', '/^.?$/']],
772 72
                'Import/ldi_escaped' => [['validateByRegex', '/^.$/']],
773 72
                'Import/ldi_terminated' => [['validateByRegex', '/^.$/']],
774 72
                'Import/skip_queries' => 'validateNonNegativeNumber',
775 72
                'InsertRows' => 'validatePositiveNumber',
776 72
                'NumRecentTables' => 'validateNonNegativeNumber',
777 72
                'NumFavoriteTables' => 'validateNonNegativeNumber',
778 72
                'LimitChars' => 'validatePositiveNumber',
779 72
                'LoginCookieValidity' => 'validatePositiveNumber',
780 72
                'LoginCookieStore' => 'validateNonNegativeNumber',
781 72
                'MaxDbList' => 'validatePositiveNumber',
782 72
                'MaxNavigationItems' => 'validatePositiveNumber',
783 72
                'MaxCharactersInDisplayedSQL' => 'validatePositiveNumber',
784 72
                'MaxRows' => 'validatePositiveNumber',
785 72
                'MaxSizeForInputField' => 'validatePositiveNumber',
786 72
                'MinSizeForInputField' => 'validateNonNegativeNumber',
787 72
                'MaxTableList' => 'validatePositiveNumber',
788 72
                'MaxRoutineList' => 'validatePositiveNumber',
789 72
                'MemoryLimit' => [['validateByRegex', '/^(-1|(\d+(?:[kmg])?))$/i']],
790 72
                'NavigationTreeDisplayItemFilterMinimum' => 'validatePositiveNumber',
791 72
                'NavigationTreeTableLevel' => 'validatePositiveNumber',
792 72
                'NavigationWidth' => 'validateNonNegativeNumber',
793 72
                'QueryHistoryMax' => 'validatePositiveNumber',
794 72
                'RepeatCells' => 'validateNonNegativeNumber',
795 72
                'Server' => 'validateServer',
796 72
                'Server_pmadb' => 'validatePMAStorage',
797 72
                'Servers/1/port' => 'validatePortNumber',
798 72
                'Servers/1/hide_db' => 'validateRegex',
799 72
                'TextareaCols' => 'validatePositiveNumber',
800 72
                'TextareaRows' => 'validatePositiveNumber',
801 72
                'TrustedProxies' => 'validateTrustedProxies',
802 72
            ],
803
804
            /**
805
             * Additional validators used for user preferences
806
             */
807 72
            '_userValidators' => [
808 72
                'MaxDbList' => [['validateUpperBound', 'value:MaxDbList']],
809 72
                'MaxTableList' => [['validateUpperBound', 'value:MaxTableList']],
810 72
                'MaxRoutineList' => [['validateUpperBound', 'value:MaxRoutineList']],
811 72
                'QueryHistoryMax' => [['validateUpperBound', 'value:QueryHistoryMax']],
812 72
            ],
813 72
        ];
814
    }
815
}
816