Translated using Weblate (Portuguese (Brazil))
[phpmyadmin.git] / src / Url.php
blob29df2e928774da74e1f57fbe34f8aac023a79431
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;
12 use function base64_decode;
13 use function base64_encode;
14 use function htmlspecialchars;
15 use function http_build_query;
16 use function in_array;
17 use function ini_get;
18 use function is_array;
19 use function is_string;
20 use function json_encode;
21 use function method_exists;
22 use function str_contains;
23 use function strtr;
25 /**
26 * Static methods for URL/hidden inputs generating
28 class Url
30 private static string|null $inputArgSeparator = null;
32 /**
33 * Generates text with hidden inputs.
35 * @see Url::getCommon()
37 * @param string|mixed[] $db optional database name (can also be an array of parameters)
38 * @param string $table optional table name
39 * @param string|mixed[] $skip do not generate a hidden field for this parameter (can be an array of strings)
41 * @return string string with input fields
43 public static function getHiddenInputs(
44 string|array $db = '',
45 string $table = '',
46 string|array $skip = [],
47 ): string {
48 if (is_array($db)) {
49 $params =& $db;
50 } else {
51 $params = [];
52 if ($db !== '') {
53 $params['db'] = $db;
56 if ($table !== '') {
57 $params['table'] = $table;
61 $config = Config::getInstance();
62 if (Current::$server > 0 && Current::$server !== $config->settings['ServerDefault']) {
63 $params['server'] = Current::$server;
66 if (empty($config->getCookie('pma_lang')) && ! empty($GLOBALS['lang'])) {
67 $params['lang'] = $GLOBALS['lang'];
70 if (! is_array($skip)) {
71 if (isset($params[$skip])) {
72 unset($params[$skip]);
74 } else {
75 foreach ($skip as $skipping) {
76 if (! isset($params[$skipping])) {
77 continue;
80 unset($params[$skipping]);
84 return self::getHiddenFields($params);
87 /**
88 * create hidden form fields from array with name => value
90 * <code>
91 * $values = array(
92 * 'aaa' => aaa,
93 * 'bbb' => array(
94 * 'bbb_0',
95 * 'bbb_1',
96 * ),
97 * 'ccc' => array(
98 * 'a' => 'ccc_a',
99 * 'b' => 'ccc_b',
100 * ),
101 * );
102 * echo Url::getHiddenFields($values);
104 * // produces:
105 * <input type="hidden" name="aaa" Value="aaa">
106 * <input type="hidden" name="bbb[0]" Value="bbb_0">
107 * <input type="hidden" name="bbb[1]" Value="bbb_1">
108 * <input type="hidden" name="ccc[a]" Value="ccc_a">
109 * <input type="hidden" name="ccc[b]" Value="ccc_b">
110 * </code>
112 * @param mixed[] $values hidden values
113 * @param string $pre prefix
114 * @param bool $isToken if token already added in hidden input field
116 * @return string form fields of type hidden
118 public static function getHiddenFields(array $values, string $pre = '', bool $isToken = false): string
120 $fields = '';
122 /* Always include token in plain forms */
123 if ($isToken === false && isset($_SESSION[' PMA_token '])) {
124 $values['token'] = $_SESSION[' PMA_token '];
127 foreach ($values as $name => $value) {
128 if ($pre !== '') {
129 $name = $pre . '[' . $name . ']';
132 if (is_array($value)) {
133 $fields .= self::getHiddenFields($value, $name, true);
134 } else {
135 // do not generate an ending "\n" because
136 // Url::getHiddenInputs() is sometimes called
137 // from a JS document.write()
138 $fields .= '<input type="hidden" name="' . htmlspecialchars((string) $name)
139 . '" value="' . htmlspecialchars((string) $value) . '">';
143 return $fields;
147 * Generates text with URL parameters.
149 * <code>
150 * $params['myparam'] = 'myvalue';
151 * $params['db'] = 'mysql';
152 * $params['table'] = 'rights';
153 * // note the missing ?
154 * echo 'script.php' . Url::getCommon($params);
155 * // produces with cookies enabled:
156 * // script.php?myparam=myvalue&db=mysql&table=rights
157 * // with cookies disabled:
158 * // script.php?server=1&lang=en&myparam=myvalue&db=mysql
159 * // &table=rights
161 * // note the missing ?
162 * echo 'script.php' . Url::getCommon();
163 * // produces with cookies enabled:
164 * // script.php
165 * // with cookies disabled:
166 * // script.php?server=1&lang=en
167 * </code>
169 * @param array<string,int|string|bool> $params optional, Contains an associative array with url params
170 * @param string $divider optional character to use instead of '?'
171 * @param bool $encrypt whether to encrypt URL params
173 * @return string string with URL parameters
175 public static function getCommon(array $params = [], string $divider = '?', bool $encrypt = true): string
177 return self::getCommonRaw($params, $divider, $encrypt);
181 * Generates text with URL parameters.
183 * <code>
184 * $params['myparam'] = 'myvalue';
185 * $params['db'] = 'mysql';
186 * $params['table'] = 'rights';
187 * // note the missing ?
188 * echo 'script.php' . Url::getCommon($params);
189 * // produces with cookies enabled:
190 * // script.php?myparam=myvalue&db=mysql&table=rights
191 * // with cookies disabled:
192 * // script.php?server=1&lang=en&myparam=myvalue&db=mysql
193 * // &table=rights
195 * // note the missing ?
196 * echo 'script.php' . Url::getCommon();
197 * // produces with cookies enabled:
198 * // script.php
199 * // with cookies disabled:
200 * // script.php?server=1&lang=en
201 * </code>
203 * @param array<string|int,int|string|bool> $params optional, Contains an associative array with url params
204 * @param string $divider optional character to use instead of '?'
205 * @param bool $encrypt whether to encrypt URL params
207 * @return string string with URL parameters
209 public static function getCommonRaw(array $params = [], string $divider = '?', bool $encrypt = true): string
211 // avoid overwriting when creating navigation panel links to servers
212 $config = Config::getInstance();
213 if (
214 Current::$server > 0
215 && Current::$server !== $config->settings['ServerDefault']
216 && ! isset($params['server'])
217 && ! $config->get('is_setup')
219 $params['server'] = Current::$server;
222 // Can be null when the user is missing an extension.
223 if (empty($config->getCookie('pma_lang')) && ! empty($GLOBALS['lang'])) {
224 $params['lang'] = $GLOBALS['lang'];
227 $query = self::buildHttpQuery($params, $encrypt);
229 if (($divider !== '?' && $divider !== self::getArgSeparator()) || $query !== '') {
230 return $divider . $query;
233 return '';
237 * @param array<int|string, mixed> $params
238 * @param bool $encrypt whether to encrypt URL params
240 public static function buildHttpQuery(array $params, bool $encrypt = true): string
242 if ($params === []) {
243 return '';
246 $separator = self::getArgSeparator();
248 if (! $encrypt || ! Config::getInstance()->get('URLQueryEncryption')) {
249 return http_build_query($params, '', $separator);
252 $data = $params;
253 $keys = [
254 'db',
255 'table',
256 'field',
257 'sql_query',
258 'sql_signature',
259 'where_clause',
260 'goto',
261 'back',
262 'message_to_show',
263 'username',
264 'hostname',
265 'dbname',
266 'tablename',
268 $paramsToEncrypt = [];
269 foreach ($params as $paramKey => $paramValue) {
270 if (! in_array($paramKey, $keys)) {
271 continue;
274 $paramsToEncrypt[$paramKey] = $paramValue;
275 unset($data[$paramKey]);
278 if ($paramsToEncrypt !== []) {
279 $data['eq'] = self::encryptQuery((string) json_encode($paramsToEncrypt));
282 return http_build_query($data, '', $separator);
285 public static function encryptQuery(string $query): string
287 $crypto = new Crypto();
289 return strtr(base64_encode($crypto->encrypt($query)), '+/', '-_');
292 public static function decryptQuery(string $query): string|null
294 $crypto = new Crypto();
296 return $crypto->decrypt(base64_decode(strtr($query, '-_', '+/')));
300 * Returns url separator character used for separating url parts.
302 * Extracted from 'arg_separator.input' as set in php.ini, but prefers '&' and ';'.
304 * @see https://www.php.net/manual/en/ini.core.php#ini.arg-separator.input
305 * @see https://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2.2
307 public static function getArgSeparator(): string
309 if (is_string(self::$inputArgSeparator)) {
310 return self::$inputArgSeparator;
313 $separator = self::getArgSeparatorValueFromIni();
314 if (! is_string($separator) || $separator === '' || str_contains($separator, '&')) {
315 return self::$inputArgSeparator = '&';
318 if (str_contains($separator, ';')) {
319 return self::$inputArgSeparator = ';';
322 // uses first character
323 return self::$inputArgSeparator = $separator[0];
326 /** @return string|false */
327 private static function getArgSeparatorValueFromIni(): string|bool
330 * @psalm-suppress ArgumentTypeCoercion
331 * @phpstan-ignore-next-line
333 if (method_exists('PhpMyAdmin\Tests\UrlTest', 'getInputArgSeparator')) {
334 // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName
335 return \PhpMyAdmin\Tests\UrlTest::getInputArgSeparator();
338 return ini_get('arg_separator.input');
342 * @param string $route Route to use
343 * @param mixed[] $additionalParameters Additional URL parameters
345 public static function getFromRoute(string $route, array $additionalParameters = []): string
347 return 'index.php?route=' . $route . self::getCommon($additionalParameters, self::getArgSeparator());