1<?php 2/* vim: set expandtab sw=4 ts=4 sts=4: */ 3/** 4 * PhpMyAdmin\Server\Status\Data class 5 * Used by server_status_*.php pages 6 * 7 * @package PhpMyAdmin 8 */ 9namespace PhpMyAdmin\Server\Status; 10 11use PhpMyAdmin\Url; 12 13/** 14 * This class provides data about the server status 15 * 16 * All properties of the class are read-only 17 * 18 * TODO: Use lazy initialisation for some of the properties 19 * since not all of the server_status_*.php pages need 20 * all the data that this class provides. 21 * 22 * @package PhpMyAdmin 23 */ 24class Data 25{ 26 public $status; 27 public $sections; 28 public $variables; 29 public $used_queries; 30 public $allocationMap; 31 public $links; 32 public $db_isLocal; 33 public $section; 34 public $sectionUsed; 35 public $selfUrl; 36 public $dataLoaded; 37 38 /** 39 * An empty setter makes the above properties read-only 40 * 41 * @param string $a key 42 * @param mixed $b value 43 * 44 * @return void 45 */ 46 public function __set($a, $b) 47 { 48 // Discard everything 49 } 50 51 /** 52 * Gets the allocations for constructor 53 * 54 * @return array 55 */ 56 private function _getAllocations() 57 { 58 return array( 59 // variable name => section 60 // variable names match when they begin with the given string 61 62 'Com_' => 'com', 63 'Innodb_' => 'innodb', 64 'Ndb_' => 'ndb', 65 'Handler_' => 'handler', 66 'Qcache_' => 'qcache', 67 'Threads_' => 'threads', 68 'Slow_launch_threads' => 'threads', 69 70 'Binlog_cache_' => 'binlog_cache', 71 'Created_tmp_' => 'created_tmp', 72 'Key_' => 'key', 73 74 'Delayed_' => 'delayed', 75 'Not_flushed_delayed_rows' => 'delayed', 76 77 'Flush_commands' => 'query', 78 'Last_query_cost' => 'query', 79 'Slow_queries' => 'query', 80 'Queries' => 'query', 81 'Prepared_stmt_count' => 'query', 82 83 'Select_' => 'select', 84 'Sort_' => 'sort', 85 86 'Open_tables' => 'table', 87 'Opened_tables' => 'table', 88 'Open_table_definitions' => 'table', 89 'Opened_table_definitions' => 'table', 90 'Table_locks_' => 'table', 91 92 'Rpl_status' => 'repl', 93 'Slave_' => 'repl', 94 95 'Tc_' => 'tc', 96 97 'Ssl_' => 'ssl', 98 99 'Open_files' => 'files', 100 'Open_streams' => 'files', 101 'Opened_files' => 'files', 102 ); 103 } 104 105 /** 106 * Gets the sections for constructor 107 * 108 * @return array 109 */ 110 private function _getSections() 111 { 112 return array( 113 // section => section name (description) 114 'com' => 'Com', 115 'query' => __('SQL query'), 116 'innodb' => 'InnoDB', 117 'ndb' => 'NDB', 118 'handler' => __('Handler'), 119 'qcache' => __('Query cache'), 120 'threads' => __('Threads'), 121 'binlog_cache' => __('Binary log'), 122 'created_tmp' => __('Temporary data'), 123 'delayed' => __('Delayed inserts'), 124 'key' => __('Key cache'), 125 'select' => __('Joins'), 126 'repl' => __('Replication'), 127 'sort' => __('Sorting'), 128 'table' => __('Tables'), 129 'tc' => __('Transaction coordinator'), 130 'files' => __('Files'), 131 'ssl' => 'SSL', 132 'other' => __('Other') 133 ); 134 } 135 136 /** 137 * Gets the links for constructor 138 * 139 * @return array 140 */ 141 private function _getLinks() 142 { 143 $links = array(); 144 // variable or section name => (name => url) 145 146 $links['table'][__('Flush (close) all tables')] = [ 147 'url' => $this->selfUrl, 148 'params' => Url::getCommon(['flush' => 'TABLES'], ''), 149 ]; 150 $links['table'][__('Show open tables')] = [ 151 'url' => 'sql.php', 152 'params' => Url::getCommon([ 153 'sql_query' => 'SHOW OPEN TABLES', 154 'goto' => $this->selfUrl, 155 ], ''), 156 ]; 157 158 if ($GLOBALS['replication_info']['master']['status']) { 159 $links['repl'][__('Show slave hosts')] = [ 160 'url' => 'sql.php', 161 'params' => Url::getCommon([ 162 'sql_query' => 'SHOW SLAVE HOSTS', 163 'goto' => $this->selfUrl, 164 ], ''), 165 ]; 166 $links['repl'][__('Show master status')] = [ 167 'url' => '#replication_master', 168 'params' => '', 169 ]; 170 } 171 if ($GLOBALS['replication_info']['slave']['status']) { 172 $links['repl'][__('Show slave status')] = [ 173 'url' => '#replication_slave', 174 'params' => '', 175 ]; 176 } 177 178 $links['repl']['doc'] = 'replication'; 179 180 $links['qcache'][__('Flush query cache')] = [ 181 'url' => $this->selfUrl, 182 'params' => Url::getCommon(['flush' => 'QUERY CACHE'], ''), 183 ]; 184 $links['qcache']['doc'] = 'query_cache'; 185 186 $links['threads']['doc'] = 'mysql_threads'; 187 188 $links['key']['doc'] = 'myisam_key_cache'; 189 190 $links['binlog_cache']['doc'] = 'binary_log'; 191 192 $links['Slow_queries']['doc'] = 'slow_query_log'; 193 194 $links['innodb'][__('Variables')] = [ 195 'url' => 'server_engines.php', 196 'params' => Url::getCommon(['engine' => 'InnoDB'], ''), 197 ]; 198 $links['innodb'][__('InnoDB Status')] = [ 199 'url' => 'server_engines.php', 200 'params' => Url::getCommon([ 201 'engine' => 'InnoDB', 202 'page' => 'Status', 203 ], ''), 204 ]; 205 $links['innodb']['doc'] = 'innodb'; 206 207 return($links); 208 } 209 210 /** 211 * Calculate some values 212 * 213 * @param array $server_status contains results of SHOW GLOBAL STATUS 214 * @param array $server_variables contains results of SHOW GLOBAL VARIABLES 215 * 216 * @return array $server_status 217 */ 218 private function _calculateValues(array $server_status, array $server_variables) 219 { 220 // Key_buffer_fraction 221 if (isset($server_status['Key_blocks_unused']) 222 && isset($server_variables['key_cache_block_size']) 223 && isset($server_variables['key_buffer_size']) 224 && $server_variables['key_buffer_size'] != 0 225 ) { 226 $server_status['Key_buffer_fraction_%'] 227 = 100 228 - $server_status['Key_blocks_unused'] 229 * $server_variables['key_cache_block_size'] 230 / $server_variables['key_buffer_size'] 231 * 100; 232 } elseif (isset($server_status['Key_blocks_used']) 233 && isset($server_variables['key_buffer_size']) 234 && $server_variables['key_buffer_size'] != 0 235 ) { 236 $server_status['Key_buffer_fraction_%'] 237 = $server_status['Key_blocks_used'] 238 * 1024 239 / $server_variables['key_buffer_size']; 240 } 241 242 // Ratio for key read/write 243 if (isset($server_status['Key_writes']) 244 && isset($server_status['Key_write_requests']) 245 && $server_status['Key_write_requests'] > 0 246 ) { 247 $key_writes = $server_status['Key_writes']; 248 $key_write_requests = $server_status['Key_write_requests']; 249 $server_status['Key_write_ratio_%'] 250 = 100 * $key_writes / $key_write_requests; 251 } 252 253 if (isset($server_status['Key_reads']) 254 && isset($server_status['Key_read_requests']) 255 && $server_status['Key_read_requests'] > 0 256 ) { 257 $key_reads = $server_status['Key_reads']; 258 $key_read_requests = $server_status['Key_read_requests']; 259 $server_status['Key_read_ratio_%'] 260 = 100 * $key_reads / $key_read_requests; 261 } 262 263 // Threads_cache_hitrate 264 if (isset($server_status['Threads_created']) 265 && isset($server_status['Connections']) 266 && $server_status['Connections'] > 0 267 ) { 268 269 $server_status['Threads_cache_hitrate_%'] 270 = 100 - $server_status['Threads_created'] 271 / $server_status['Connections'] * 100; 272 } 273 return $server_status; 274 } 275 276 /** 277 * Sort variables into arrays 278 * 279 * @param array $server_status contains results of SHOW GLOBAL STATUS 280 * @param array $allocations allocations for sections 281 * @param array $allocationMap map variables to their section 282 * @param array $sectionUsed is a section used? 283 * @param array $used_queries used queries 284 * 285 * @return array ($allocationMap, $sectionUsed, $used_queries) 286 */ 287 private function _sortVariables( 288 array $server_status, array $allocations, array $allocationMap, array $sectionUsed, 289 array $used_queries 290 ) { 291 foreach ($server_status as $name => $value) { 292 $section_found = false; 293 foreach ($allocations as $filter => $section) { 294 if (mb_strpos($name, $filter) !== false) { 295 $allocationMap[$name] = $section; 296 $sectionUsed[$section] = true; 297 $section_found = true; 298 if ($section == 'com' && $value > 0) { 299 $used_queries[$name] = $value; 300 } 301 break; // Only exits inner loop 302 } 303 } 304 if (! $section_found) { 305 $allocationMap[$name] = 'other'; 306 $sectionUsed['other'] = true; 307 } 308 } 309 return array($allocationMap, $sectionUsed, $used_queries); 310 } 311 312 /** 313 * Constructor 314 */ 315 public function __construct() 316 { 317 $this->selfUrl = basename($GLOBALS['PMA_PHP_SELF']); 318 319 // get status from server 320 $server_status_result = $GLOBALS['dbi']->tryQuery('SHOW GLOBAL STATUS'); 321 $server_status = array(); 322 if ($server_status_result === false) { 323 $this->dataLoaded = false; 324 } else { 325 $this->dataLoaded = true; 326 while ($arr = $GLOBALS['dbi']->fetchRow($server_status_result)) { 327 $server_status[$arr[0]] = $arr[1]; 328 } 329 $GLOBALS['dbi']->freeResult($server_status_result); 330 } 331 332 // for some calculations we require also some server settings 333 $server_variables = $GLOBALS['dbi']->fetchResult( 334 'SHOW GLOBAL VARIABLES', 0, 1 335 ); 336 337 // cleanup of some deprecated values 338 $server_status = self::cleanDeprecated($server_status); 339 340 // calculate some values 341 $server_status = $this->_calculateValues( 342 $server_status, $server_variables 343 ); 344 345 // split variables in sections 346 $allocations = $this->_getAllocations(); 347 348 $sections = $this->_getSections(); 349 350 // define some needful links/commands 351 $links = $this->_getLinks(); 352 353 // Variable to contain all com_ variables (query statistics) 354 $used_queries = array(); 355 356 // Variable to map variable names to their respective section name 357 // (used for js category filtering) 358 $allocationMap = array(); 359 360 // Variable to mark used sections 361 $sectionUsed = array(); 362 363 // sort vars into arrays 364 list( 365 $allocationMap, $sectionUsed, $used_queries 366 ) = $this->_sortVariables( 367 $server_status, $allocations, $allocationMap, $sectionUsed, 368 $used_queries 369 ); 370 371 // admin commands are not queries (e.g. they include COM_PING, 372 // which is excluded from $server_status['Questions']) 373 unset($used_queries['Com_admin_commands']); 374 375 // Set all class properties 376 $this->db_isLocal = false; 377 $serverHostToLower = mb_strtolower( 378 $GLOBALS['cfg']['Server']['host'] 379 ); 380 if ($serverHostToLower === 'localhost' 381 || $GLOBALS['cfg']['Server']['host'] === '127.0.0.1' 382 || $GLOBALS['cfg']['Server']['host'] === '::1' 383 ) { 384 $this->db_isLocal = true; 385 } 386 $this->status = $server_status; 387 $this->sections = $sections; 388 $this->variables = $server_variables; 389 $this->used_queries = $used_queries; 390 $this->allocationMap = $allocationMap; 391 $this->links = $links; 392 $this->sectionUsed = $sectionUsed; 393 } 394 395 /** 396 * cleanup of some deprecated values 397 * 398 * @param array $server_status status array to process 399 * 400 * @return array 401 */ 402 public static function cleanDeprecated(array $server_status) 403 { 404 $deprecated = array( 405 'Com_prepare_sql' => 'Com_stmt_prepare', 406 'Com_execute_sql' => 'Com_stmt_execute', 407 'Com_dealloc_sql' => 'Com_stmt_close', 408 ); 409 foreach ($deprecated as $old => $new) { 410 if (isset($server_status[$old]) && isset($server_status[$new])) { 411 unset($server_status[$old]); 412 } 413 } 414 return $server_status; 415 } 416 417 /** 418 * Generates menu HTML 419 * 420 * @return string 421 */ 422 public function getMenuHtml() 423 { 424 $url_params = Url::getCommon(); 425 $items = array( 426 array( 427 'name' => __('Server'), 428 'url' => 'server_status.php' 429 ), 430 array( 431 'name' => __('Processes'), 432 'url' => 'server_status_processes.php' 433 ), 434 array( 435 'name' => __('Query statistics'), 436 'url' => 'server_status_queries.php' 437 ), 438 array( 439 'name' => __('All status variables'), 440 'url' => 'server_status_variables.php' 441 ), 442 array( 443 'name' => __('Monitor'), 444 'url' => 'server_status_monitor.php' 445 ), 446 array( 447 'name' => __('Advisor'), 448 'url' => 'server_status_advisor.php' 449 ) 450 ); 451 452 $retval = '<ul id="topmenu2">'; 453 foreach ($items as $item) { 454 $class = ''; 455 if ($item['url'] === $this->selfUrl) { 456 $class = ' class="tabactive"'; 457 } 458 $retval .= '<li>'; 459 $retval .= '<a' . $class; 460 $retval .= ' href="' . $item['url'] . $url_params . '">'; 461 $retval .= $item['name']; 462 $retval .= '</a>'; 463 $retval .= '</li>'; 464 } 465 $retval .= '</ul>'; 466 $retval .= '<div class="clearfloat"></div>'; 467 468 return $retval; 469 } 470 471 /** 472 * Builds a <select> list for refresh rates 473 * 474 * @param string $name Name of select 475 * @param int $defaultRate Currently chosen rate 476 * @param array $refreshRates List of refresh rates 477 * 478 * @return string 479 */ 480 public static function getHtmlForRefreshList($name, 481 $defaultRate = 5, 482 array $refreshRates = array(1, 2, 5, 10, 20, 40, 60, 120, 300, 600) 483 ) { 484 $return = '<select name="' . $name . '" id="id_' . $name 485 . '" class="refreshRate">'; 486 foreach ($refreshRates as $rate) { 487 $selected = ($rate == $defaultRate)?' selected="selected"':''; 488 $return .= '<option value="' . $rate . '"' . $selected . '>'; 489 if ($rate < 60) { 490 $return .= sprintf( 491 _ngettext('%d second', '%d seconds', $rate), $rate 492 ); 493 } else { 494 $rate = $rate / 60; 495 $return .= sprintf( 496 _ngettext('%d minute', '%d minutes', $rate), $rate 497 ); 498 } 499 $return .= '</option>'; 500 } 501 $return .= '</select>'; 502 return $return; 503 } 504} 505