3 * Static methods for URL/hidden inputs generating
6 declare(strict_types
=1);
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
;
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
;
26 * Static methods for URL/hidden inputs generating
30 private static string|
null $inputArgSeparator = null;
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 = '',
46 string|
array $skip = [],
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]);
75 foreach ($skip as $skipping) {
76 if (! isset($params[$skipping])) {
80 unset($params[$skipping]);
84 return self
::getHiddenFields($params);
88 * create hidden form fields from array with name => value
102 * echo Url::getHiddenFields($values);
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">
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
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) {
129 $name = $pre . '[' . $name . ']';
132 if (is_array($value)) {
133 $fields .= self
::getHiddenFields($value, $name, true);
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) . '">';
147 * Generates text with URL parameters.
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
161 * // note the missing ?
162 * echo 'script.php' . Url::getCommon();
163 * // produces with cookies enabled:
165 * // with cookies disabled:
166 * // script.php?server=1&lang=en
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.
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
195 * // note the missing ?
196 * echo 'script.php' . Url::getCommon();
197 * // produces with cookies enabled:
199 * // with cookies disabled:
200 * // script.php?server=1&lang=en
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();
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;
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 === []) {
246 $separator = self
::getArgSeparator();
248 if (! $encrypt ||
! Config
::getInstance()->get('URLQueryEncryption')) {
249 return http_build_query($params, '', $separator);
268 $paramsToEncrypt = [];
269 foreach ($params as $paramKey => $paramValue) {
270 if (! in_array($paramKey, $keys)) {
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());