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