Data.php 16 KB

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