PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/classes/Server/Status/Data.php

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