3 * Static methods for URL/hidden inputs generating
6 declare(strict_types
=1);
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
;
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
;
27 * Static methods for URL/hidden inputs generating
31 private static string|
null $inputArgSeparator = null;
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 = '',
47 string|
array $skip = [],
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]);
76 foreach ($skip as $skipping) {
77 if (! isset($params[$skipping])) {
81 unset($params[$skipping]);
85 return self
::getHiddenFields($params);
89 * create hidden form fields from array with name => value
103 * echo Url::getHiddenFields($values);
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">
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
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) {
130 $name = $pre . '[' . $name . ']';
133 if (is_array($value)) {
134 $fields .= self
::getHiddenFields($value, $name, true);
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) . '">';
148 * Generates text with URL parameters.
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
162 * // note the missing ?
163 * echo 'script.php' . Url::getCommon();
164 * // produces with cookies enabled:
166 * // with cookies disabled:
167 * // script.php?server=1&lang=en
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.
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
196 * // note the missing ?
197 * echo 'script.php' . Url::getCommon();
198 * // produces with cookies enabled:
200 * // with cookies disabled:
201 * // script.php?server=1&lang=en
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();
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;
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 === []) {
247 $separator = self
::getArgSeparator();
249 if (! $encrypt ||
! Config
::getInstance()->get('URLQueryEncryption')) {
250 return http_build_query($params, '', $separator);
269 $paramsToEncrypt = [];
270 foreach ($params as $paramKey => $paramValue) {
271 if (! in_array($paramKey, $keys)) {
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);