1<?php 2 3declare(strict_types=1); 4 5namespace PhpMyAdmin\Controllers\Server; 6 7use PhpMyAdmin\Controllers\AbstractController; 8use PhpMyAdmin\DatabaseInterface; 9use PhpMyAdmin\Html\Generator; 10use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider; 11use PhpMyAdmin\Response; 12use PhpMyAdmin\Template; 13use PhpMyAdmin\Url; 14use PhpMyAdmin\Util; 15use function header; 16use function htmlspecialchars; 17use function implode; 18use function in_array; 19use function is_numeric; 20use function mb_strtolower; 21use function pow; 22use function preg_match; 23use function str_replace; 24use function strtolower; 25use function trim; 26 27/** 28 * Handles viewing and editing server variables 29 */ 30class VariablesController extends AbstractController 31{ 32 /** @var DatabaseInterface */ 33 private $dbi; 34 35 /** 36 * @param Response $response 37 * @param DatabaseInterface $dbi 38 */ 39 public function __construct($response, Template $template, $dbi) 40 { 41 parent::__construct($response, $template); 42 $this->dbi = $dbi; 43 } 44 45 public function index(): void 46 { 47 global $err_url; 48 49 $params = ['filter' => $_GET['filter'] ?? null]; 50 $err_url = Url::getFromRoute('/'); 51 52 if ($this->dbi->isSuperUser()) { 53 $this->dbi->selectDb('mysql'); 54 } 55 56 $filterValue = ! empty($params['filter']) ? $params['filter'] : ''; 57 58 $this->addScriptFiles(['server/variables.js']); 59 60 $variables = []; 61 $serverVarsResult = $this->dbi->tryQuery('SHOW SESSION VARIABLES;'); 62 if ($serverVarsResult !== false) { 63 $serverVarsSession = []; 64 while ($arr = $this->dbi->fetchRow($serverVarsResult)) { 65 $serverVarsSession[$arr[0]] = $arr[1]; 66 } 67 $this->dbi->freeResult($serverVarsResult); 68 69 $serverVars = $this->dbi->fetchResult('SHOW GLOBAL VARIABLES;', 0, 1); 70 71 // list of static (i.e. non-editable) system variables 72 $staticVariables = ServerVariablesProvider::getImplementation()->getStaticVariables(); 73 74 foreach ($serverVars as $name => $value) { 75 $hasSessionValue = isset($serverVarsSession[$name]) 76 && $serverVarsSession[$name] !== $value; 77 $docLink = Generator::linkToVarDocumentation( 78 $name, 79 $this->dbi->isMariaDB(), 80 str_replace('_', ' ', $name) 81 ); 82 83 [$formattedValue, $isEscaped] = $this->formatVariable($name, $value); 84 if ($hasSessionValue) { 85 [$sessionFormattedValue] = $this->formatVariable( 86 $name, 87 $serverVarsSession[$name] 88 ); 89 } 90 91 $variables[] = [ 92 'name' => $name, 93 'is_editable' => ! in_array(strtolower($name), $staticVariables), 94 'doc_link' => $docLink, 95 'value' => $formattedValue, 96 'is_escaped' => $isEscaped, 97 'has_session_value' => $hasSessionValue, 98 'session_value' => $sessionFormattedValue ?? null, 99 ]; 100 } 101 } 102 103 $this->render('server/variables/index', [ 104 'variables' => $variables, 105 'filter_value' => $filterValue, 106 'is_superuser' => $this->dbi->isSuperUser(), 107 'is_mariadb' => $this->dbi->isMariaDB(), 108 ]); 109 } 110 111 /** 112 * Handle the AJAX request for a single variable value 113 * 114 * @param array $params Request parameters 115 */ 116 public function getValue(array $params): void 117 { 118 if (! $this->response->isAjax()) { 119 return; 120 } 121 122 // Send with correct charset 123 header('Content-Type: text/html; charset=UTF-8'); 124 // Do not use double quotes inside the query to avoid a problem 125 // when server is running in ANSI_QUOTES sql_mode 126 $varValue = $this->dbi->fetchSingleRow( 127 'SHOW GLOBAL VARIABLES WHERE Variable_name=\'' 128 . $this->dbi->escapeString($params['name']) . '\';', 129 'NUM' 130 ); 131 132 $json = [ 133 'message' => $varValue[1], 134 ]; 135 136 $variableType = ServerVariablesProvider::getImplementation()->getVariableType($params['name']); 137 138 if ($variableType === 'byte') { 139 $json['message'] = implode( 140 ' ', 141 Util::formatByteDown($varValue[1], 3, 3) 142 ); 143 } 144 145 $this->response->addJSON($json); 146 } 147 148 /** 149 * Handle the AJAX request for setting value for a single variable 150 * 151 * @param array $vars Request parameters 152 */ 153 public function setValue(array $vars): void 154 { 155 $params = [ 156 'varName' => $vars['name'], 157 'varValue' => $_POST['varValue'] ?? null, 158 ]; 159 160 if (! $this->response->isAjax()) { 161 return; 162 } 163 164 $value = (string) $params['varValue']; 165 $variableName = (string) $params['varName']; 166 $matches = []; 167 $variableType = ServerVariablesProvider::getImplementation()->getVariableType($variableName); 168 169 if ($variableType === 'byte' && preg_match( 170 '/^\s*(\d+(\.\d+)?)\s*(mb|kb|mib|kib|gb|gib)\s*$/i', 171 $value, 172 $matches 173 )) { 174 $exp = [ 175 'kb' => 1, 176 'kib' => 1, 177 'mb' => 2, 178 'mib' => 2, 179 'gb' => 3, 180 'gib' => 3, 181 ]; 182 $value = (float) $matches[1] * pow( 183 1024, 184 $exp[mb_strtolower($matches[3])] 185 ); 186 } else { 187 $value = $this->dbi->escapeString($value); 188 } 189 190 if (! is_numeric($value)) { 191 $value = "'" . $value . "'"; 192 } 193 194 $json = []; 195 if (! preg_match('/[^a-zA-Z0-9_]+/', $params['varName']) 196 && $this->dbi->query( 197 'SET GLOBAL ' . $params['varName'] . ' = ' . $value 198 ) 199 ) { 200 // Some values are rounded down etc. 201 $varValue = $this->dbi->fetchSingleRow( 202 'SHOW GLOBAL VARIABLES WHERE Variable_name="' 203 . $this->dbi->escapeString($params['varName']) 204 . '";', 205 'NUM' 206 ); 207 [$formattedValue, $isHtmlFormatted] = $this->formatVariable( 208 $params['varName'], 209 $varValue[1] 210 ); 211 212 if ($isHtmlFormatted === false) { 213 $json['variable'] = htmlspecialchars($formattedValue); 214 } else { 215 $json['variable'] = $formattedValue; 216 } 217 } else { 218 $this->response->setRequestStatus(false); 219 $json['error'] = __('Setting variable failed'); 220 } 221 222 $this->response->addJSON($json); 223 } 224 225 /** 226 * Format Variable 227 * 228 * @param string $name variable name 229 * @param int|string $value variable value 230 * 231 * @return array formatted string and bool if string is HTML formatted 232 */ 233 private function formatVariable($name, $value): array 234 { 235 $isHtmlFormatted = false; 236 $formattedValue = $value; 237 238 if (is_numeric($value)) { 239 $variableType = ServerVariablesProvider::getImplementation()->getVariableType($name); 240 241 if ($variableType === 'byte') { 242 $isHtmlFormatted = true; 243 $formattedValue = trim( 244 $this->template->render( 245 'server/variables/format_variable', 246 [ 247 'valueTitle' => Util::formatNumber($value, 0), 248 'value' => implode(' ', Util::formatByteDown($value, 3, 3)), 249 ] 250 ) 251 ); 252 } else { 253 $formattedValue = Util::formatNumber($value, 0); 254 } 255 } 256 257 return [ 258 $formattedValue, 259 $isHtmlFormatted, 260 ]; 261 } 262} 263