Data.php 14 KB

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