' . __('Error') . '
';
// For security reasons, if the MySQL refuses the connection, the query
// is hidden so no details are revealed.
if ((!empty($sql_query)) && (!(mb_strstr($sql_query, 'connect')))) {
// Static analysis errors.
if (!empty($errors)) {
$error_msg .= '
' . __('Static analysis:')
. '
';
$error_msg .= '
' . sprintf(
__('%d errors were found during analysis.'),
count($errors)
) . '
';
$error_msg .= '
';
$error_msg .= implode(
ParserError::format(
$errors,
'- %2$s (near "%4$s" at position %5$d)
'
)
);
$error_msg .= '
';
}
// Display the SQL query and link to MySQL documentation.
$error_msg .= '
' . __('SQL query:') . '' . "\n";
$formattedSqlToLower = mb_strtolower($formatted_sql);
// TODO: Show documentation for all statement types.
if (mb_strstr($formattedSqlToLower, 'select')) {
// please show me help to the error on select
$error_msg .= self::showMySQLDocu('SELECT');
}
if ($is_modify_link) {
$_url_params = array(
'sql_query' => $sql_query,
'show_query' => 1,
);
if (strlen($table) > 0) {
$_url_params['db'] = $db;
$_url_params['table'] = $table;
$doedit_goto = '';
} elseif (strlen($db) > 0) {
$_url_params['db'] = $db;
$doedit_goto = '';
} else {
$doedit_goto = '';
}
$error_msg .= $doedit_goto
. self::getIcon('b_edit', __('Edit'))
. '';
}
$error_msg .= '
' . "\n"
. '
' . "\n"
. $formatted_sql . "\n"
. '
' . "\n";
}
// Display server's error.
if (!empty($server_msg)) {
$server_msg = preg_replace(
"@((\015\012)|(\015)|(\012)){3,}@",
"\n\n",
$server_msg
);
// Adds a link to MySQL documentation.
$error_msg .= '
' . "\n"
. ' ' . __('MySQL said: ') . ''
. self::showMySQLDocu('Error-messages-server')
. "\n"
. '
' . "\n";
// The error message will be displayed within a CODE segment.
// To preserve original formatting, but allow word-wrapping,
// a couple of replacements are done.
// All non-single blanks and TAB-characters are replaced with their
// HTML-counterpart
$server_msg = str_replace(
array(' ', "\t"),
array(' ', ' '),
$server_msg
);
// Replace line breaks
$server_msg = nl2br($server_msg);
$error_msg .= '
' . $server_msg . '';
}
$error_msg .= '
';
$_SESSION['Import_message']['message'] = $error_msg;
if (!$exit) {
return $error_msg;
}
/**
* If this is an AJAX request, there is no "Back" link and
* `Response()` is used to send the response.
*/
$response = Response::getInstance();
if ($response->isAjax()) {
$response->setRequestStatus(false);
$response->addJSON('message', $error_msg);
exit;
}
if (!empty($back_url)) {
if (mb_strstr($back_url, '?')) {
$back_url .= '&no_history=true';
} else {
$back_url .= '?no_history=true';
}
$_SESSION['Import_message']['go_back_url'] = $back_url;
$error_msg .= '' . "\n\n";
}
exit($error_msg);
}
/**
* Check the correct row count
*
* @param string $db the db name
* @param array $table the table infos
*
* @return int $rowCount the possibly modified row count
*
*/
private static function _checkRowCount($db, array $table)
{
$rowCount = 0;
if ($table['Rows'] === null) {
// Do not check exact row count here,
// if row count is invalid possibly the table is defect
// and this would break the navigation panel;
// but we can check row count if this is a view or the
// information_schema database
// since Table::countRecords() returns a limited row count
// in this case.
// set this because Table::countRecords() can use it
$tbl_is_view = $table['TABLE_TYPE'] == 'VIEW';
if ($tbl_is_view || $GLOBALS['dbi']->isSystemSchema($db)) {
$rowCount = $GLOBALS['dbi']
->getTable($db, $table['Name'])
->countRecords();
}
}
return $rowCount;
}
/**
* returns array with tables of given db with extended information and grouped
*
* @param string $db name of db
* @param string $tables name of tables
* @param integer $limit_offset list offset
* @param int|bool $limit_count max tables to return
*
* @return array (recursive) grouped table list
*/
public static function getTableList(
$db,
$tables = null,
$limit_offset = 0,
$limit_count = false
) {
$sep = $GLOBALS['cfg']['NavigationTreeTableSeparator'];
if ($tables === null) {
$tables = $GLOBALS['dbi']->getTablesFull(
$db,
'',
false,
$limit_offset,
$limit_count
);
if ($GLOBALS['cfg']['NaturalOrder']) {
uksort($tables, 'strnatcasecmp');
}
}
if (count($tables) < 1) {
return $tables;
}
$default = array(
'Name' => '',
'Rows' => 0,
'Comment' => '',
'disp_name' => '',
);
$table_groups = array();
foreach ($tables as $table_name => $table) {
$table['Rows'] = self::_checkRowCount($db, $table);
// in $group we save the reference to the place in $table_groups
// where to store the table info
if ($GLOBALS['cfg']['NavigationTreeEnableGrouping']
&& $sep && mb_strstr($table_name, $sep)
) {
$parts = explode($sep, $table_name);
$group =& $table_groups;
$i = 0;
$group_name_full = '';
$parts_cnt = count($parts) - 1;
while (($i < $parts_cnt)
&& ($i < $GLOBALS['cfg']['NavigationTreeTableLevel'])
) {
$group_name = $parts[$i] . $sep;
$group_name_full .= $group_name;
if (! isset($group[$group_name])) {
$group[$group_name] = array();
$group[$group_name]['is' . $sep . 'group'] = true;
$group[$group_name]['tab' . $sep . 'count'] = 1;
$group[$group_name]['tab' . $sep . 'group']
= $group_name_full;
} elseif (! isset($group[$group_name]['is' . $sep . 'group'])) {
$table = $group[$group_name];
$group[$group_name] = array();
$group[$group_name][$group_name] = $table;
$group[$group_name]['is' . $sep . 'group'] = true;
$group[$group_name]['tab' . $sep . 'count'] = 1;
$group[$group_name]['tab' . $sep . 'group']
= $group_name_full;
} else {
$group[$group_name]['tab' . $sep . 'count']++;
}
$group =& $group[$group_name];
$i++;
}
} else {
if (! isset($table_groups[$table_name])) {
$table_groups[$table_name] = array();
}
$group =& $table_groups;
}
$table['disp_name'] = $table['Name'];
$group[$table_name] = array_merge($default, $table);
}
return $table_groups;
}
/* ----------------------- Set of misc functions ----------------------- */
/**
* Adds backquotes on both sides of a database, table or field name.
* and escapes backquotes inside the name with another backquote
*
* example:
* ' . "\n";
}
if ($message instanceof Message) {
if (isset($GLOBALS['special_message'])) {
$message->addText($GLOBALS['special_message']);
unset($GLOBALS['special_message']);
}
$retval .= $message->getDisplay();
} else {
$retval .= '
';
$retval .= Sanitize::sanitize($message);
if (isset($GLOBALS['special_message'])) {
$retval .= Sanitize::sanitize($GLOBALS['special_message']);
unset($GLOBALS['special_message']);
}
$retval .= '
';
}
if ($render_sql) {
$query_too_big = false;
$queryLength = mb_strlen($sql_query);
if ($queryLength > $cfg['MaxCharactersInDisplayedSQL']) {
// when the query is large (for example an INSERT of binary
// data), the parser chokes; so avoid parsing the query
$query_too_big = true;
$query_base = mb_substr(
$sql_query,
0,
$cfg['MaxCharactersInDisplayedSQL']
) . '[...]';
} else {
$query_base = $sql_query;
}
// Html format the query to be displayed
// If we want to show some sql code it is easiest to create it here
/* SQL-Parser-Analyzer */
if (! empty($GLOBALS['show_as_php'])) {
$new_line = '\\n"
' . "\n" . ' . "';
$query_base = htmlspecialchars(addslashes($query_base));
$query_base = preg_replace(
'/((\015\012)|(\015)|(\012))/',
$new_line,
$query_base
);
$query_base = '
' . "\n"
. '$sql = "' . $query_base . '";' . "\n"
. '';
} elseif ($query_too_big) {
$query_base = '
' . "\n" .
htmlspecialchars($query_base) .
'';
} else {
$query_base = self::formatSql($query_base);
}
// Prepares links that may be displayed to edit/explain the query
// (don't go to default pages, we must go to the page
// where the query box is available)
// Basic url query part
$url_params = array();
if (! isset($GLOBALS['db'])) {
$GLOBALS['db'] = '';
}
if (strlen($GLOBALS['db']) > 0) {
$url_params['db'] = $GLOBALS['db'];
if (strlen($GLOBALS['table']) > 0) {
$url_params['table'] = $GLOBALS['table'];
$edit_link = 'tbl_sql.php';
} else {
$edit_link = 'db_sql.php';
}
} else {
$edit_link = 'server_sql.php';
}
// Want to have the query explained
// but only explain a SELECT (that has not been explained)
/* SQL-Parser-Analyzer */
$explain_link = '';
$is_select = preg_match('@^SELECT[[:space:]]+@i', $sql_query);
if (! empty($cfg['SQLQuery']['Explain']) && ! $query_too_big) {
$explain_params = $url_params;
if ($is_select) {
$explain_params['sql_query'] = 'EXPLAIN ' . $sql_query;
$explain_link = ' [ '
. self::linkOrButton(
'import.php' . Url::getCommon($explain_params),
__('Explain SQL')
) . ' ]';
} elseif (preg_match(
'@^EXPLAIN[[:space:]]+SELECT[[:space:]]+@i',
$sql_query
)) {
$explain_params['sql_query']
= mb_substr($sql_query, 8);
$explain_link = ' [ '
. self::linkOrButton(
'import.php' . Url::getCommon($explain_params),
__('Skip Explain SQL')
) . ']';
$url = 'https://mariadb.org/explain_analyzer/analyze/'
. '?client=phpMyAdmin&raw_explain='
. urlencode(self::_generateRowQueryOutput($sql_query));
$explain_link .= ' ['
. self::linkOrButton(
htmlspecialchars('url.php?url=' . urlencode($url)),
sprintf(__('Analyze Explain at %s'), 'mariadb.org'),
array(),
'_blank'
) . ' ]';
}
} //show explain
$url_params['sql_query'] = $sql_query;
$url_params['show_query'] = 1;
// even if the query is big and was truncated, offer the chance
// to edit it (unless it's enormous, see linkOrButton() )
if (! empty($cfg['SQLQuery']['Edit'])
&& empty($GLOBALS['show_as_php'])
) {
$edit_link .= Url::getCommon($url_params);
$edit_link = ' [ '
. self::linkOrButton($edit_link, __('Edit'))
. ' ]';
} else {
$edit_link = '';
}
// Also we would like to get the SQL formed in some nice
// php-code
if (! empty($cfg['SQLQuery']['ShowAsPHP']) && ! $query_too_big) {
if (! empty($GLOBALS['show_as_php'])) {
$php_link = ' [ '
. self::linkOrButton(
'import.php' . Url::getCommon($url_params),
__('Without PHP code')
)
. ' ]';
$php_link .= ' [ '
. self::linkOrButton(
'import.php' . Url::getCommon($url_params),
__('Submit query')
)
. ' ]';
} else {
$php_params = $url_params;
$php_params['show_as_php'] = 1;
$php_link = ' [ '
. self::linkOrButton(
'import.php' . Url::getCommon($php_params),
__('Create PHP code')
)
. ' ]';
}
} else {
$php_link = '';
} //show as php
// Refresh query
if (! empty($cfg['SQLQuery']['Refresh'])
&& ! isset($GLOBALS['show_as_php']) // 'Submit query' does the same
&& preg_match('@^(SELECT|SHOW)[[:space:]]+@i', $sql_query)
) {
$refresh_link = 'import.php' . Url::getCommon($url_params);
$refresh_link = ' [ '
. self::linkOrButton($refresh_link, __('Refresh')) . ']';
} else {
$refresh_link = '';
} //refresh
$retval .= '
';
$retval .= $query_base;
$retval .= '
';
$retval .= '
';
$retval .= '';
/**
* TODO: Should we have $cfg['SQLQuery']['InlineEdit']?
*/
if (! empty($cfg['SQLQuery']['Edit'])
&& ! $query_too_big
&& empty($GLOBALS['show_as_php'])
) {
$inline_edit_link = ' ['
. self::linkOrButton(
'#',
_pgettext('Inline edit query', 'Edit inline'),
array('class' => 'inline_edit_sql')
)
. ']';
} else {
$inline_edit_link = '';
}
$retval .= $inline_edit_link . $edit_link . $explain_link . $php_link
. $refresh_link;
$retval .= '
';
$retval .= '
';
}
return $retval;
} // end of the 'getMessage()' function
/**
* Execute an EXPLAIN query and formats results similar to MySQL command line
* utility.
*
* @param string $sqlQuery EXPLAIN query
*
* @return string query resuls
*/
private static function _generateRowQueryOutput($sqlQuery)
{
$ret = '';
$result = $GLOBALS['dbi']->query($sqlQuery);
if ($result) {
$devider = '+';
$columnNames = '|';
$fieldsMeta = $GLOBALS['dbi']->getFieldsMeta($result);
foreach ($fieldsMeta as $meta) {
$devider .= '---+';
$columnNames .= ' ' . $meta->name . ' |';
}
$devider .= "\n";
$ret .= $devider . $columnNames . "\n" . $devider;
while ($row = $GLOBALS['dbi']->fetchRow($result)) {
$values = '|';
foreach ($row as $value) {
if (is_null($value)) {
$value = 'NULL';
}
$values .= ' ' . $value . ' |';
}
$ret .= $values . "\n";
}
$ret .= $devider;
}
return $ret;
}
/**
* Verifies if current MySQL server supports profiling
*
* @access public
*
* @return boolean whether profiling is supported
*/
public static function profilingSupported()
{
if (!self::cacheExists('profiling_supported')) {
// 5.0.37 has profiling but for example, 5.1.20 does not
// (avoid a trip to the server for MySQL before 5.0.37)
// and do not set a constant as we might be switching servers
if ($GLOBALS['dbi']->fetchValue("SELECT @@have_profiling")
) {
self::cacheSet('profiling_supported', true);
} else {
self::cacheSet('profiling_supported', false);
}
}
return self::cacheGet('profiling_supported');
}
/**
* Formats $value to byte view
*
* @param double|int $value the value to format
* @param int $limes the sensitiveness
* @param int $comma the number of decimals to retain
*
* @return array the formatted value and its unit
*
* @access public
*/
public static function formatByteDown($value, $limes = 6, $comma = 0)
{
if ($value === null) {
return null;
}
$byteUnits = array(
/* l10n: shortcuts for Byte */
__('B'),
/* l10n: shortcuts for Kilobyte */
__('KiB'),
/* l10n: shortcuts for Megabyte */
__('MiB'),
/* l10n: shortcuts for Gigabyte */
__('GiB'),
/* l10n: shortcuts for Terabyte */
__('TiB'),
/* l10n: shortcuts for Petabyte */
__('PiB'),
/* l10n: shortcuts for Exabyte */
__('EiB')
);
$dh = pow(10, $comma);
$li = pow(10, $limes);
$unit = $byteUnits[0];
for ($d = 6, $ex = 15; $d >= 1; $d--, $ex-=3) {
$unitSize = $li * pow(10, $ex);
if (isset($byteUnits[$d]) && $value >= $unitSize) {
// use 1024.0 to avoid integer overflow on 64-bit machines
$value = round($value / (pow(1024, $d) / $dh)) /$dh;
$unit = $byteUnits[$d];
break 1;
} // end if
} // end for
if ($unit != $byteUnits[0]) {
// if the unit is not bytes (as represented in current language)
// reformat with max length of 5
// 4th parameter=true means do not reformat if value < 1
$return_value = self::formatNumber($value, 5, $comma, true, false);
} else {
// do not reformat, just handle the locale
$return_value = self::formatNumber($value, 0);
}
return array(trim($return_value), $unit);
} // end of the 'formatByteDown' function
/**
* Formats $value to the given length and appends SI prefixes
* with a $length of 0 no truncation occurs, number is only formatted
* to the current locale
*
* examples:
* ';
if ($frame != 'frame_navigation') {
$list_navigator_html .= __('Page number:');
}
// Move to the beginning or to the previous page
if ($pos > 0) {
$caption1 = ''; $caption2 = '';
if (self::showIcons('TableNavigationLinksMode')) {
$caption1 .= '<< ';
$caption2 .= '< ';
}
if (self::showText('TableNavigationLinksMode')) {
$caption1 .= _pgettext('First page', 'Begin');
$caption2 .= _pgettext('Previous page', 'Previous');
}
$title1 = ' title="' . _pgettext('First page', 'Begin') . '"';
$title2 = ' title="' . _pgettext('Previous page', 'Previous') . '"';
$_url_params[$name] = 0;
$list_navigator_html .= '
' . $caption1
. '';
$_url_params[$name] = $pos - $max_count;
$list_navigator_html .= '
'
. $caption2 . '';
}
$list_navigator_html .= '
';
if ($pos + $max_count < $count) {
$caption3 = ''; $caption4 = '';
if (self::showText('TableNavigationLinksMode')) {
$caption3 .= _pgettext('Next page', 'Next');
$caption4 .= _pgettext('Last page', 'End');
}
if (self::showIcons('TableNavigationLinksMode')) {
$caption3 .= ' >';
$caption4 .= ' >>';
if (! self::showText('TableNavigationLinksMode')) {
}
}
$title3 = ' title="' . _pgettext('Next page', 'Next') . '"';
$title4 = ' title="' . _pgettext('Last page', 'End') . '"';
$_url_params[$name] = $pos + $max_count;
$list_navigator_html .= '
' . $caption3
. '';
$_url_params[$name] = floor($count / $max_count) * $max_count;
if ($_url_params[$name] == $count) {
$_url_params[$name] = $count - $max_count;
}
$list_navigator_html .= '
'
. $caption4 . '';
}
$list_navigator_html .= '
' . "\n";
}
return $list_navigator_html;
}
/**
* replaces %u in given path with current user name
*
* example:
* on which to apply the effect
* @param string $message the message to show as a link
* @param string|null $overrideDefault override InitialSlidersState config
*
* @return string html div element
*
*/
public static function getDivForSliderEffect($id = '', $message = '', $overrideDefault = null)
{
return Template::get('div_for_slider_effect')->render([
'id' => $id,
'initial_sliders_state' => ($overrideDefault != null) ? $overrideDefault : $GLOBALS['cfg']['InitialSlidersState'],
'message' => $message,
]);
}
/**
* Creates an AJAX sliding toggle button
* (or and equivalent form when AJAX is disabled)
*
* @param string $action The URL for the request to be executed
* @param string $select_name The name for the dropdown box
* @param array $options An array of options (see PhpMyAdmin\Rte\Footer)
* @param string $callback A JS snippet to execute when the request is
* successfully processed
*
* @return string HTML code for the toggle button
*/
public static function toggleButton($action, $select_name, array $options, $callback)
{
// Do the logic first
$link = "$action&" . urlencode($select_name) . "=";
$link_on = $link . urlencode($options[1]['value']);
$link_off = $link . urlencode($options[0]['value']);
if ($options[1]['selected'] == true) {
$state = 'on';
} elseif ($options[0]['selected'] == true) {
$state = 'off';
} else {
$state = 'on';
}
return Template::get('toggle_button')->render(
[
'pma_theme_image' => $GLOBALS['pmaThemeImage'],
'text_dir' => $GLOBALS['text_dir'],
'link_on' => $link_on,
'link_off' => $link_off,
'toggle_on' => $options[1]['label'],
'toggle_off' => $options[0]['label'],
'callback' => $callback,
'state' => $state
]);
} // end toggleButton()
/**
* Clears cache content which needs to be refreshed on user change.
*
* @return void
*/
public static function clearUserCache()
{
self::cacheUnset('is_superuser');
self::cacheUnset('is_createuser');
self::cacheUnset('is_grantuser');
}
/**
* Calculates session cache key
*
* @return string
*/
public static function cacheKey()
{
if (isset($GLOBALS['cfg']['Server']['user'])) {
return 'server_' . $GLOBALS['server'] . '_' . $GLOBALS['cfg']['Server']['user'];
}
return 'server_' . $GLOBALS['server'];
}
/**
* Verifies if something is cached in the session
*
* @param string $var variable name
*
* @return boolean
*/
public static function cacheExists($var)
{
return isset($_SESSION['cache'][self::cacheKey()][$var]);
}
/**
* Gets cached information from the session
*
* @param string $var variable name
* @param \Closure $callback callback to fetch the value
*
* @return mixed
*/
public static function cacheGet($var, $callback = null)
{
if (self::cacheExists($var)) {
return $_SESSION['cache'][self::cacheKey()][$var];
}
if ($callback) {
$val = $callback();
self::cacheSet($var, $val);
return $val;
}
return null;
}
/**
* Caches information in the session
*
* @param string $var variable name
* @param mixed $val value
*
* @return mixed
*/
public static function cacheSet($var, $val = null)
{
$_SESSION['cache'][self::cacheKey()][$var] = $val;
}
/**
* Removes cached information from the session
*
* @param string $var variable name
*
* @return void
*/
public static function cacheUnset($var)
{
unset($_SESSION['cache'][self::cacheKey()][$var]);
}
/**
* Converts a bit value to printable format;
* in MySQL a BIT field can be from 1 to 64 bits so we need this
* function because in PHP, decbin() supports only 32 bits
* on 32-bit servers
*
* @param integer $value coming from a BIT field
* @param integer $length length
*
* @return string the printable value
*/
public static function printableBitValue($value, $length)
{
// if running on a 64-bit server or the length is safe for decbin()
if (PHP_INT_SIZE == 8 || $length < 33) {
$printable = decbin($value);
} else {
// FIXME: does not work for the leftmost bit of a 64-bit value
$i = 0;
$printable = '';
while ($value >= pow(2, $i)) {
++$i;
}
if ($i != 0) {
--$i;
}
while ($i >= 0) {
if ($value - pow(2, $i) < 0) {
$printable = '0' . $printable;
} else {
$printable = '1' . $printable;
$value = $value - pow(2, $i);
}
--$i;
}
$printable = strrev($printable);
}
$printable = str_pad($printable, $length, '0', STR_PAD_LEFT);
return $printable;
}
/**
* Verifies whether the value contains a non-printable character
*
* @param string $value value
*
* @return integer
*/
public static function containsNonPrintableAscii($value)
{
return preg_match('@[^[:print:]]@', $value);
}
/**
* Converts a BIT type default value
* for example, b'010' becomes 010
*
* @param string $bit_default_value value
*
* @return string the converted value
*/
public static function convertBitDefaultValue($bit_default_value)
{
return rtrim(ltrim(htmlspecialchars_decode($bit_default_value, ENT_QUOTES), "b'"), "'");
}
/**
* Extracts the various parts from a column spec
*
* @param string $columnspec Column specification
*
* @return array associative array containing type, spec_in_brackets
* and possibly enum_set_values (another array)
*/
public static function extractColumnSpec($columnspec)
{
$first_bracket_pos = mb_strpos($columnspec, '(');
if ($first_bracket_pos) {
$spec_in_brackets = chop(
mb_substr(
$columnspec,
$first_bracket_pos + 1,
mb_strrpos($columnspec, ')') - $first_bracket_pos - 1
)
);
// convert to lowercase just to be sure
$type = mb_strtolower(
chop(mb_substr($columnspec, 0, $first_bracket_pos))
);
} else {
// Split trailing attributes such as unsigned,
// binary, zerofill and get data type name
$type_parts = explode(' ', $columnspec);
$type = mb_strtolower($type_parts[0]);
$spec_in_brackets = '';
}
if ('enum' == $type || 'set' == $type) {
// Define our working vars
$enum_set_values = self::parseEnumSetValues($columnspec, false);
$printtype = $type
. '(' . str_replace("','", "', '", $spec_in_brackets) . ')';
$binary = false;
$unsigned = false;
$zerofill = false;
} else {
$enum_set_values = array();
/* Create printable type name */
$printtype = mb_strtolower($columnspec);
// Strip the "BINARY" attribute, except if we find "BINARY(" because
// this would be a BINARY or VARBINARY column type;
// by the way, a BLOB should not show the BINARY attribute
// because this is not accepted in MySQL syntax.
if (preg_match('@binary@', $printtype)
&& ! preg_match('@binary[\(]@', $printtype)
) {
$printtype = preg_replace('@binary@', '', $printtype);
$binary = true;
} else {
$binary = false;
}
$printtype = preg_replace(
'@zerofill@', '', $printtype, -1, $zerofill_cnt
);
$zerofill = ($zerofill_cnt > 0);
$printtype = preg_replace(
'@unsigned@', '', $printtype, -1, $unsigned_cnt
);
$unsigned = ($unsigned_cnt > 0);
$printtype = trim($printtype);
}
$attribute = ' ';
if ($binary) {
$attribute = 'BINARY';
}
if ($unsigned) {
$attribute = 'UNSIGNED';
}
if ($zerofill) {
$attribute = 'UNSIGNED ZEROFILL';
}
$can_contain_collation = false;
if (! $binary
&& preg_match(
"@^(char|varchar|text|tinytext|mediumtext|longtext|set|enum)@", $type
)
) {
$can_contain_collation = true;
}
// for the case ENUM('–','“')
$displayed_type = htmlspecialchars($printtype);
if (mb_strlen($printtype) > $GLOBALS['cfg']['LimitChars']) {
$displayed_type = '
';
$displayed_type .= htmlspecialchars(
mb_substr(
$printtype, 0, $GLOBALS['cfg']['LimitChars']
) . '...'
);
$displayed_type .= '';
}
return array(
'type' => $type,
'spec_in_brackets' => $spec_in_brackets,
'enum_set_values' => $enum_set_values,
'print_type' => $printtype,
'binary' => $binary,
'unsigned' => $unsigned,
'zerofill' => $zerofill,
'attribute' => $attribute,
'can_contain_collation' => $can_contain_collation,
'displayed_type' => $displayed_type
);
}
/**
* Verifies if this table's engine supports foreign keys
*
* @param string $engine engine
*
* @return boolean
*/
public static function isForeignKeySupported($engine)
{
$engine = strtoupper($engine);
if (($engine == 'INNODB') || ($engine == 'PBXT')) {
return true;
} elseif ($engine == 'NDBCLUSTER' || $engine == 'NDB') {
$ndbver = strtolower(
$GLOBALS['dbi']->fetchValue("SELECT @@ndb_version_string")
);
if (substr($ndbver, 0, 4) == 'ndb-') {
$ndbver = substr($ndbver, 4);
}
return version_compare($ndbver, 7.3, '>=');
}
return false;
}
/**
* Is Foreign key check enabled?
*
* @return bool
*/
public static function isForeignKeyCheck()
{
if ($GLOBALS['cfg']['DefaultForeignKeyChecks'] === 'enable') {
return true;
} elseif ($GLOBALS['cfg']['DefaultForeignKeyChecks'] === 'disable') {
return false;
}
return ($GLOBALS['dbi']->getVariable('FOREIGN_KEY_CHECKS') == 'ON');
}
/**
* Get HTML for Foreign key check checkbox
*
* @return string HTML for checkbox
*/
public static function getFKCheckbox()
{
return Template::get('fk_checkbox')->render([
'checked' => self::isForeignKeyCheck(),
]);
}
/**
* Handle foreign key check request
*
* @return bool Default foreign key checks value
*/
public static function handleDisableFKCheckInit()
{
$default_fk_check_value
= $GLOBALS['dbi']->getVariable('FOREIGN_KEY_CHECKS') == 'ON';
if (isset($_REQUEST['fk_checks'])) {
if (empty($_REQUEST['fk_checks'])) {
// Disable foreign key checks
$GLOBALS['dbi']->setVariable('FOREIGN_KEY_CHECKS', 'OFF');
} else {
// Enable foreign key checks
$GLOBALS['dbi']->setVariable('FOREIGN_KEY_CHECKS', 'ON');
}
} // else do nothing, go with default
return $default_fk_check_value;
}
/**
* Cleanup changes done for foreign key check
*
* @param bool $default_fk_check_value original value for 'FOREIGN_KEY_CHECKS'
*
* @return void
*/
public static function handleDisableFKCheckCleanup($default_fk_check_value)
{
$GLOBALS['dbi']->setVariable(
'FOREIGN_KEY_CHECKS', $default_fk_check_value ? 'ON' : 'OFF'
);
}
/**
* Converts GIS data to Well Known Text format
*
* @param string $data GIS data
* @param bool $includeSRID Add SRID to the WKT
*
* @return string GIS data in Well Know Text format
*/
public static function asWKT($data, $includeSRID = false)
{
// Convert to WKT format
$hex = bin2hex($data);
$spatialAsText = 'ASTEXT';
$spatialSrid = 'SRID';
if ($GLOBALS['dbi']->getVersion() >= 50600) {
$spatialAsText = 'ST_ASTEXT';
$spatialSrid = 'ST_SRID';
}
$wktsql = "SELECT $spatialAsText(x'" . $hex . "')";
if ($includeSRID) {
$wktsql .= ", $spatialSrid(x'" . $hex . "')";
}
$wktresult = $GLOBALS['dbi']->tryQuery(
$wktsql
);
$wktarr = $GLOBALS['dbi']->fetchRow($wktresult, 0);
$wktval = isset($wktarr[0]) ? $wktarr[0] : null;
if ($includeSRID) {
$srid = isset($wktarr[1]) ? $wktarr[1] : null;
$wktval = "'" . $wktval . "'," . $srid;
}
@$GLOBALS['dbi']->freeResult($wktresult);
return $wktval;
}
/**
* If the string starts with a \r\n pair (0x0d0a) add an extra \n
*
* @param string $string string
*
* @return string with the chars replaced
*/
public static function duplicateFirstNewline($string)
{
$first_occurence = mb_strpos($string, "\r\n");
if ($first_occurence === 0) {
$string = "\n" . $string;
}
return $string;
}
/**
* Get the action word corresponding to a script name
* in order to display it as a title in navigation panel
*
* @param string $target a valid value for $cfg['NavigationTreeDefaultTabTable'],
* $cfg['NavigationTreeDefaultTabTable2'],
* $cfg['DefaultTabTable'] or $cfg['DefaultTabDatabase']
*
* @return string Title for the $cfg value
*/
public static function getTitleForTarget($target)
{
$mapping = array(
'structure' => __('Structure'),
'sql' => __('SQL'),
'search' =>__('Search'),
'insert' =>__('Insert'),
'browse' => __('Browse'),
'operations' => __('Operations'),
// For backward compatiblity
// Values for $cfg['DefaultTabTable']
'tbl_structure.php' => __('Structure'),
'tbl_sql.php' => __('SQL'),
'tbl_select.php' =>__('Search'),
'tbl_change.php' =>__('Insert'),
'sql.php' => __('Browse'),
// Values for $cfg['DefaultTabDatabase']
'db_structure.php' => __('Structure'),
'db_sql.php' => __('SQL'),
'db_search.php' => __('Search'),
'db_operations.php' => __('Operations'),
);
return isset($mapping[$target]) ? $mapping[$target] : false;
}
/**
* Get the script name corresponding to a plain English config word
* in order to append in links on navigation and main panel
*
* @param string $target a valid value for
* $cfg['NavigationTreeDefaultTabTable'],
* $cfg['NavigationTreeDefaultTabTable2'],
* $cfg['DefaultTabTable'], $cfg['DefaultTabDatabase'] or
* $cfg['DefaultTabServer']
* @param string $location one out of 'server', 'table', 'database'
*
* @return string script name corresponding to the config word
*/
public static function getScriptNameForOption($target, $location)
{
if ($location == 'server') {
// Values for $cfg['DefaultTabServer']
switch ($target) {
case 'welcome':
return 'index.php';
case 'databases':
return 'server_databases.php';
case 'status':
return 'server_status.php';
case 'variables':
return 'server_variables.php';
case 'privileges':
return 'server_privileges.php';
}
} elseif ($location == 'database') {
// Values for $cfg['DefaultTabDatabase']
switch ($target) {
case 'structure':
return 'db_structure.php';
case 'sql':
return 'db_sql.php';
case 'search':
return 'db_search.php';
case 'operations':
return 'db_operations.php';
}
} elseif ($location == 'table') {
// Values for $cfg['DefaultTabTable'],
// $cfg['NavigationTreeDefaultTabTable'] and
// $cfg['NavigationTreeDefaultTabTable2']
switch ($target) {
case 'structure':
return 'tbl_structure.php';
case 'sql':
return 'tbl_sql.php';
case 'search':
return 'tbl_select.php';
case 'insert':
return 'tbl_change.php';
case 'browse':
return 'sql.php';
}
}
return $target;
}
/**
* Formats user string, expanding @VARIABLES@, accepting strftime format
* string.
*
* @param string $string Text where to do expansion.
* @param array|string $escape Function to call for escaping variable values.
* Can also be an array of:
* - the escape method name
* - the class that contains the method
* - location of the class (for inclusion)
* @param array $updates Array with overrides for default parameters
* (obtained from GLOBALS).
*
* @return string
*/
public static function expandUserString(
$string, $escape = null, array $updates = array()
) {
/* Content */
$vars = array();
$vars['http_host'] = Core::getenv('HTTP_HOST');
$vars['server_name'] = $GLOBALS['cfg']['Server']['host'];
$vars['server_verbose'] = $GLOBALS['cfg']['Server']['verbose'];
if (empty($GLOBALS['cfg']['Server']['verbose'])) {
$vars['server_verbose_or_name'] = $GLOBALS['cfg']['Server']['host'];
} else {
$vars['server_verbose_or_name'] = $GLOBALS['cfg']['Server']['verbose'];
}
$vars['database'] = $GLOBALS['db'];
$vars['table'] = $GLOBALS['table'];
$vars['phpmyadmin_version'] = 'phpMyAdmin ' . PMA_VERSION;
/* Update forced variables */
foreach ($updates as $key => $val) {
$vars[$key] = $val;
}
/* Replacement mapping */
/*
* The __VAR__ ones are for backward compatibility, because user
* might still have it in cookies.
*/
$replace = array(
'@HTTP_HOST@' => $vars['http_host'],
'@SERVER@' => $vars['server_name'],
'__SERVER__' => $vars['server_name'],
'@VERBOSE@' => $vars['server_verbose'],
'@VSERVER@' => $vars['server_verbose_or_name'],
'@DATABASE@' => $vars['database'],
'__DB__' => $vars['database'],
'@TABLE@' => $vars['table'],
'__TABLE__' => $vars['table'],
'@PHPMYADMIN@' => $vars['phpmyadmin_version'],
);
/* Optional escaping */
if (! is_null($escape)) {
if (is_array($escape)) {
$escape_class = new $escape[1];
$escape_method = $escape[0];
}
foreach ($replace as $key => $val) {
if (is_array($escape)) {
$replace[$key] = $escape_class->$escape_method($val);
} else {
$replace[$key] = ($escape == 'backquote')
? self::$escape($val)
: $escape($val);
}
}
}
/* Backward compatibility in 3.5.x */
if (mb_strpos($string, '@FIELDS@') !== false) {
$string = strtr($string, array('@FIELDS@' => '@COLUMNS@'));
}
/* Fetch columns list if required */
if (mb_strpos($string, '@COLUMNS@') !== false) {
$columns_list = $GLOBALS['dbi']->getColumns(
$GLOBALS['db'], $GLOBALS['table']
);
// sometimes the table no longer exists at this point
if (! is_null($columns_list)) {
$column_names = array();
foreach ($columns_list as $column) {
if (! is_null($escape)) {
$column_names[] = self::$escape($column['Field']);
} else {
$column_names[] = $column['Field'];
}
}
$replace['@COLUMNS@'] = implode(',', $column_names);
} else {
$replace['@COLUMNS@'] = '*';
}
}
/* Do the replacement */
return strtr(strftime($string), $replace);
}
/**
* Prepare the form used to browse anywhere on the local server for a file to
* import
*
* @param string $max_upload_size maximum upload size
*
* @return String
*/
public static function getBrowseUploadFileBlock($max_upload_size)
{
$block_html = '';
if ($GLOBALS['is_upload'] && ! empty($GLOBALS['cfg']['UploadDir'])) {
$block_html .= '