Utilities.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Query;
  4. use PhpMyAdmin\Error;
  5. use PhpMyAdmin\Url;
  6. use function array_slice;
  7. use function debug_backtrace;
  8. use function explode;
  9. use function htmlspecialchars;
  10. use function intval;
  11. use function md5;
  12. use function sprintf;
  13. use function strcasecmp;
  14. use function strnatcasecmp;
  15. use function strpos;
  16. use function strtolower;
  17. /**
  18. * Some helfull functions for common tasks related to SQL results
  19. */
  20. class Utilities
  21. {
  22. /**
  23. * Get the list of system schemas
  24. *
  25. * @return string[] list of system schemas
  26. */
  27. public static function getSystemSchemas(): array
  28. {
  29. $schemas = [
  30. 'information_schema',
  31. 'performance_schema',
  32. 'mysql',
  33. 'sys',
  34. ];
  35. $systemSchemas = [];
  36. foreach ($schemas as $schema) {
  37. if (! self::isSystemSchema($schema, true)) {
  38. continue;
  39. }
  40. $systemSchemas[] = $schema;
  41. }
  42. return $systemSchemas;
  43. }
  44. /**
  45. * Checks whether given schema is a system schema
  46. *
  47. * @param string $schema_name Name of schema (database) to test
  48. * @param bool $testForMysqlSchema Whether 'mysql' schema should
  49. * be treated the same as IS and DD
  50. */
  51. public static function isSystemSchema(
  52. string $schema_name,
  53. bool $testForMysqlSchema = false
  54. ): bool {
  55. $schema_name = strtolower($schema_name);
  56. $isMySqlSystemSchema = $schema_name === 'mysql' && $testForMysqlSchema;
  57. return $schema_name === 'information_schema'
  58. || $schema_name === 'performance_schema'
  59. || $isMySqlSystemSchema
  60. || $schema_name === 'sys';
  61. }
  62. /**
  63. * Formats database error message in a friendly way.
  64. * This is needed because some errors messages cannot
  65. * be obtained by mysql_error().
  66. *
  67. * @param int $error_number Error code
  68. * @param string $error_message Error message as returned by server
  69. *
  70. * @return string HML text with error details
  71. */
  72. public static function formatError(int $error_number, string $error_message): string
  73. {
  74. $error_message = htmlspecialchars($error_message);
  75. $error = '#' . ((string) $error_number);
  76. $separator = ' &mdash; ';
  77. if ($error_number == 2002) {
  78. $error .= ' - ' . $error_message;
  79. $error .= $separator;
  80. $error .= __(
  81. 'The server is not responding (or the local server\'s socket'
  82. . ' is not correctly configured).'
  83. );
  84. } elseif ($error_number == 2003) {
  85. $error .= ' - ' . $error_message;
  86. $error .= $separator . __('The server is not responding.');
  87. } elseif ($error_number == 1698) {
  88. $error .= ' - ' . $error_message;
  89. $error .= $separator . '<a href="' . Url::getFromRoute('/logout') . '" class="disableAjax">';
  90. $error .= __('Logout and try as another user.') . '</a>';
  91. } elseif ($error_number == 1005) {
  92. if (strpos($error_message, 'errno: 13') !== false) {
  93. $error .= ' - ' . $error_message;
  94. $error .= $separator
  95. . __(
  96. 'Please check privileges of directory containing database.'
  97. );
  98. } else {
  99. /**
  100. * InnoDB constraints, see
  101. * https://dev.mysql.com/doc/refman/5.0/en/
  102. * innodb-foreign-key-constraints.html
  103. */
  104. $error .= ' - ' . $error_message .
  105. ' (<a href="' .
  106. Url::getFromRoute('/server/engines/InnoDB/Status') .
  107. '">' . __('Details…') . '</a>)';
  108. }
  109. } else {
  110. $error .= ' - ' . $error_message;
  111. }
  112. return $error;
  113. }
  114. /**
  115. * usort comparison callback
  116. *
  117. * @param array $a first argument to sort
  118. * @param array $b second argument to sort
  119. * @param string $sortBy Key to sort by
  120. * @param string $sortOrder The order (ASC/DESC)
  121. *
  122. * @return int a value representing whether $a should be before $b in the
  123. * sorted array or not
  124. */
  125. public static function usortComparisonCallback(array $a, array $b, string $sortBy, string $sortOrder): int
  126. {
  127. global $cfg;
  128. /* No sorting when key is not present */
  129. if (! isset($a[$sortBy], $b[$sortBy])
  130. ) {
  131. return 0;
  132. }
  133. // produces f.e.:
  134. // return -1 * strnatcasecmp($a['SCHEMA_TABLES'], $b['SCHEMA_TABLES'])
  135. $compare = $cfg['NaturalOrder'] ? strnatcasecmp(
  136. $a[$sortBy],
  137. $b[$sortBy]
  138. ) : strcasecmp(
  139. $a[$sortBy],
  140. $b[$sortBy]
  141. );
  142. return ($sortOrder === 'ASC' ? 1 : -1) * $compare;
  143. }
  144. /**
  145. * Convert version string to integer.
  146. *
  147. * @param string $version MySQL server version
  148. */
  149. public static function versionToInt(string $version): int
  150. {
  151. $match = explode('.', $version);
  152. return (int) sprintf('%d%02d%02d', $match[0], $match[1], intval($match[2]));
  153. }
  154. /**
  155. * Stores query data into session data for debugging purposes
  156. *
  157. * @param string $query Query text
  158. * @param string|null $errorMessage Error message from getError()
  159. * @param object|bool $result Query result
  160. * @param int|float $time Time to execute query
  161. */
  162. public static function debugLogQueryIntoSession(string $query, ?string $errorMessage, $result, $time): void
  163. {
  164. $dbgInfo = [];
  165. if ($result === false && $errorMessage !== null) {
  166. $dbgInfo['error']
  167. = '<span class="color_red">'
  168. . htmlspecialchars($errorMessage) . '</span>';
  169. }
  170. $dbgInfo['query'] = htmlspecialchars($query);
  171. $dbgInfo['time'] = $time;
  172. // Get and slightly format backtrace, this is used
  173. // in the javascript console.
  174. // Strip call to debugLogQueryIntoSession
  175. $dbgInfo['trace'] = Error::processBacktrace(
  176. array_slice(debug_backtrace(), 1)
  177. );
  178. $dbgInfo['hash'] = md5($query);
  179. $_SESSION['debug']['queries'][] = $dbgInfo;
  180. }
  181. }