Update po files
[phpmyadmin.git] / src / Url.php
blob9b78c90f7e4a4fccfcefeee2fecf7a8ced48e878
1 <?php
2 /**
3 * Static methods for URL/hidden inputs generating
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin;
10 use PhpMyAdmin\Crypto\Crypto;
11 use PhpMyAdmin\Tests\UrlTest;
13 use function base64_decode;
14 use function base64_encode;
15 use function htmlspecialchars;
16 use function http_build_query;
17 use function in_array;
18 use function ini_get;
19 use function is_array;
20 use function is_string;
21 use function json_encode;
22 use function method_exists;
23 use function str_contains;
24 use function strtr;
26 /**
27 * Static methods for URL/hidden inputs generating
29 class Url
31 private static string|null $inputArgSeparator = null;
33 /**
34 * Generates text with hidden inputs.
36 * @see Url::getCommon()
38 * @param string|mixed[] $db optional database name (can also be an array of parameters)
39 * @param string $table optional table name
40 * @param string|mixed[] $skip do not generate a hidden field for this parameter (can be an array of strings)
42 * @return string string with input fields
44 public static function getHiddenInputs(
45 string|array $db = '',
46 string $table = '',
47 string|array $skip = [],
48 ): string {
49 if (is_array($db)) {
50 $params =& $db;
51 } else {
52 $params = [];
53 if ($db !== '') {
54 $params['db'] = $db;
57 if ($table !== '') {
58 $params['table'] = $table;
62 $config = Config::getInstance();
63 if (Current::$server > 0 && Current::$server !== $config->settings['ServerDefault']) {
64 $params['server'] = Current::$server;
67 if (empty($config->getCookie('pma_lang')) && ! empty($GLOBALS['lang'])) {
68 $params['lang'] = $GLOBALS['lang'];
71 if (! is_array($skip)) {
72 if (isset($params[$skip])) {
73 unset($params[$skip]);
75 } else {
76 foreach ($skip as $skipping) {
77 if (! isset($params[$skipping])) {
78 continue;
81 unset($params[$skipping]);
85 return self::getHiddenFields($params);
88 /**
89 * create hidden form fields from array with name => value
91 * <code>
92 * $values = array(
93 * 'aaa' => aaa,
94 * 'bbb' => array(
95 * 'bbb_0',
96 * 'bbb_1',
97 * ),
98 * 'ccc' => array(
99 * 'a' => 'ccc_a',
100 * 'b' => 'ccc_b',
101 * ),
102 * );
103 * echo Url::getHiddenFields($values);
105 * // produces:
106 * <input type="hidden" name="aaa" Value="aaa">
107 * <input type="hidden" name="bbb[0]" Value="bbb_0">
108 * <input type="hidden" name="bbb[1]" Value="bbb_1">
109 * <input type="hidden" name="ccc[a]" Value="ccc_a">
110 * <input type="hidden" name="ccc[b]" Value="ccc_b">
111 * </code>
113 * @param mixed[] $values hidden values
114 * @param string $pre prefix
115 * @param bool $isToken if token already added in hidden input field
117 * @return string form fields of type hidden
119 public static function getHiddenFields(array $values, string $pre = '', bool $isToken = false): string
121 $fields = '';
123 /* Always include token in plain forms */
124 if ($isToken === false && isset($_SESSION[' PMA_token '])) {
125 $values['token'] = $_SESSION[' PMA_token '];
128 foreach ($values as $name => $value) {
129 if ($pre !== '') {
130 $name = $pre . '[' . $name . ']';
133 if (is_array($value)) {
134 $fields .= self::getHiddenFields($value, $name, true);
135 } else {
136 // do not generate an ending "\n" because
137 // Url::getHiddenInputs() is sometimes called
138 // from a JS document.write()
139 $fields .= '<input type="hidden" name="' . htmlspecialchars((string) $name)
140 . '" value="' . htmlspecialchars((string) $value) . '">';
144 return $fields;
148 * Generates text with URL parameters.
150 * <code>
151 * $params['myparam'] = 'myvalue';
152 * $params['db'] = 'mysql';
153 * $params['table'] = 'rights';
154 * // note the missing ?
155 * echo 'script.php' . Url::getCommon($params);
156 * // produces with cookies enabled:
157 * // script.php?myparam=myvalue&db=mysql&table=rights
158 * // with cookies disabled:
159 * // script.php?server=1&lang=en&myparam=myvalue&db=mysql
160 * // &table=rights
162 * // note the missing ?
163 * echo 'script.php' . Url::getCommon();
164 * // produces with cookies enabled:
165 * // script.php
166 * // with cookies disabled:
167 * // script.php?server=1&lang=en
168 * </code>
170 * @param array<string,int|string|bool> $params optional, Contains an associative array with url params
171 * @param string $divider optional character to use instead of '?'
172 * @param bool $encrypt whether to encrypt URL params
174 * @return string string with URL parameters
176 public static function getCommon(array $params = [], string $divider = '?', bool $encrypt = true): string
178 return self::getCommonRaw($params, $divider, $encrypt);
182 * Generates text with URL parameters.
184 * <code>
185 * $params['myparam'] = 'myvalue';
186 * $params['db'] = 'mysql';
187 * $params['table'] = 'rights';
188 * // note the missing ?
189 * echo 'script.php' . Url::getCommon($params);
190 * // produces with cookies enabled:
191 * // script.php?myparam=myvalue&db=mysql&table=rights
192 * // with cookies disabled:
193 * // script.php?server=1&lang=en&myparam=myvalue&db=mysql
194 * // &table=rights
196 * // note the missing ?
197 * echo 'script.php' . Url::getCommon();
198 * // produces with cookies enabled:
199 * // script.php
200 * // with cookies disabled:
201 * // script.php?server=1&lang=en
202 * </code>
204 * @param array<string|int,int|string|bool> $params optional, Contains an associative array with url params
205 * @param string $divider optional character to use instead of '?'
206 * @param bool $encrypt whether to encrypt URL params
208 * @return string string with URL parameters
210 public static function getCommonRaw(array $params = [], string $divider = '?', bool $encrypt = true): string
212 // avoid overwriting when creating navigation panel links to servers
213 $config = Config::getInstance();
214 if (
215 Current::$server > 0
216 && Current::$server !== $config->settings['ServerDefault']
217 && ! isset($params['server'])
218 && ! $config->get('is_setup')
220 $params['server'] = Current::$server;
223 // Can be null when the user is missing an extension.
224 if (empty($config->getCookie('pma_lang')) && ! empty($GLOBALS['lang'])) {
225 $params['lang'] = $GLOBALS['lang'];
228 $query = self::buildHttpQuery($params, $encrypt);
230 if (($divider !== '?' && $divider !== self::getArgSeparator()) || $query !== '') {
231 return $divider . $query;
234 return '';
238 * @param array<int|string, mixed> $params
239 * @param bool $encrypt whether to encrypt URL params
241 public static function buildHttpQuery(array $params, bool $encrypt = true): string
243 if ($params === []) {
244 return '';
247 $separator = self::getArgSeparator();
249 if (! $encrypt || ! Config::getInstance()->get('URLQueryEncryption')) {
250 return http_build_query($params, '', $separator);
253 $data = $params;
254 $keys = [
255 'db',
256 'table',
257 'field',
258 'sql_query',
259 'sql_signature',
260 'where_clause',
261 'goto',
262 'back',
263 'message_to_show',
264 'username',
265 'hostname',
266 'dbname',
267 'tablename',
269 $paramsToEncrypt = [];
270 foreach ($params as $paramKey => $paramValue) {
271 if (! in_array($paramKey, $keys)) {
272 continue;
275 $paramsToEncrypt[$paramKey] = $paramValue;
276 unset($data[$paramKey]);
279 if ($paramsToEncrypt !== []) {
280 $data['eq'] = self::encryptQuery((string) json_encode($paramsToEncrypt));
283 return http_build_query($data, '', $separator);
286 public static function encryptQuery(string $query): string
288 $crypto = new Crypto();
290 return strtr(base64_encode($crypto->encrypt($query)), '+/', '-_');
293 public static function decryptQuery(string $query): string|null
295 $crypto = new Crypto();
297 return $crypto->decrypt(base64_decode(strtr($query, '-_', '+/')));
301 * Returns url separator character used for separating url parts.
303 * Extracted from 'arg_separator.input' as set in php.ini, but prefers '&' and ';'.
305 * @see https://www.php.net/manual/en/ini.core.php#ini.arg-separator.input
306 * @see https://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2
308 public static function getArgSeparator(): string
310 if (is_string(self::$inputArgSeparator)) {
311 return self::$inputArgSeparator;
314 $separator = self::getArgSeparatorValueFromIni();
315 if (! is_string($separator) || $separator === '' || str_contains($separator, '&')) {
316 return self::$inputArgSeparator = '&';
319 if (str_contains($separator, ';')) {
320 return self::$inputArgSeparator = ';';
323 // uses first character
324 return self::$inputArgSeparator = $separator[0];
327 /** @return string|false */
328 private static function getArgSeparatorValueFromIni(): string|bool
330 /** @phpstan-ignore-next-line */
331 if (method_exists(UrlTest::class, 'getInputArgSeparator')) {
332 return UrlTest::getInputArgSeparator();
335 return ini_get('arg_separator.input');
339 * @param string $route Route to use
340 * @param mixed[] $additionalParameters Additional URL parameters
342 public static function getFromRoute(string $route, array $additionalParameters = [], bool $encrypt = true): string
344 return 'index.php?route=' . $route . self::getCommon($additionalParameters, self::getArgSeparator(), $encrypt);