' . __('Copy') . '';
}
/**
* Get a link to variable documentation
*
* @param string $name The variable name
* @param bool $useMariaDB Use only MariaDB documentation
* @param string $text (optional) The text for the link
*
* @return string link or empty string
*/
public static function linkToVarDocumentation(
string $name,
bool $useMariaDB = false,
?string $text = null
): string {
$kbs = ServerVariablesProvider::getImplementation();
$link = $useMariaDB ? $kbs->getDocLinkByNameMariaDb($name) :
$kbs->getDocLinkByNameMysql($name);
return MySQLDocumentation::show(
$name,
false,
$link,
$text
);
}
/**
* Returns HTML code for a tooltip
*
* @param string $message the message for the tooltip
*
* @access public
*/
public static function showHint($message): string
{
if ($GLOBALS['cfg']['ShowHint']) {
$classClause = ' class="pma_hint"';
} else {
$classClause = '';
}
return ''
. self::getImage('b_help')
. '' . $message . ''
. '';
}
/**
* returns html code for db link to default db page
*
* @param string $database database
*
* @return string html link to default db page
*/
public static function getDbLink($database = ''): string
{
if ((string) $database === '') {
if ((string) $GLOBALS['db'] === '') {
return '';
}
$database = $GLOBALS['db'];
} else {
$database = Util::unescapeMysqlWildcards($database);
}
$scriptName = Util::getScriptNameForOption(
$GLOBALS['cfg']['DefaultTabDatabase'],
'database'
);
return '' . htmlspecialchars($database) . '';
}
/**
* Prepare a lightbulb hint explaining a known external bug
* that affects a functionality
*
* @param string $functionality localized message explaining the func.
* @param string $component 'mysql' (eventually, 'php')
* @param string $minimum_version of this component
* @param string $bugref bug reference for this component
*/
public static function getExternalBug(
$functionality,
$component,
$minimum_version,
$bugref
): string {
global $dbi;
$ext_but_html = '';
if (($component === 'mysql') && ($dbi->getVersion() < $minimum_version)) {
$ext_but_html .= self::showHint(
sprintf(
__('The %s functionality is affected by a known bug, see %s'),
$functionality,
Core::linkURL('https://bugs.mysql.com/') . $bugref
)
);
}
return $ext_but_html;
}
/**
* Returns an HTML IMG tag for a particular icon from a theme,
* which may be an actual file or an icon from a sprite.
* This function takes into account the ActionLinksMode
* configuration setting and wraps the image tag in a span tag.
*
* @param string $icon name of icon file
* @param string $alternate alternate text
* @param bool $force_text whether to force alternate text to be displayed
* @param bool $menu_icon whether this icon is for the menu bar or not
* @param string $control_param which directive controls the display
*
* @return string an html snippet
*/
public static function getIcon(
$icon,
$alternate = '',
$force_text = false,
$menu_icon = false,
$control_param = 'ActionLinksMode'
): string {
$include_icon = $include_text = false;
if (Util::showIcons($control_param)) {
$include_icon = true;
}
if ($force_text
|| Util::showText($control_param)
) {
$include_text = true;
}
// Sometimes use a span (we rely on this in js/sql.js). But for menu bar
// we don't need a span
$button = $menu_icon ? '' : '';
if ($include_icon) {
$button .= self::getImage($icon, $alternate);
}
if ($include_icon && $include_text) {
$button .= ' ';
}
if ($include_text) {
$button .= $alternate;
}
$button .= $menu_icon ? '' : '';
return $button;
}
/**
* Returns information about SSL status for current connection
*/
public static function getServerSSL(): string
{
$server = $GLOBALS['cfg']['Server'];
$class = 'caution';
if (! $server['ssl']) {
$message = __('SSL is not being used');
if (! empty($server['socket']) || in_array($server['host'], $GLOBALS['cfg']['MysqlSslWarningSafeHosts'])) {
$class = '';
}
} elseif (! $server['ssl_verify']) {
$message = __('SSL is used with disabled verification');
} elseif (empty($server['ssl_ca'])) {
$message = __('SSL is used without certification authority');
} else {
$class = '';
$message = __('SSL is used');
}
return '' . $message . ' ' . MySQLDocumentation::showDocumentation(
'setup',
'ssl'
);
}
/**
* Returns default function for a particular column.
*
* @param array $field Data about the column for which
* to generate the dropdown
* @param bool $insert_mode Whether the operation is 'insert'
*
* @return string An HTML snippet of a dropdown list with function
* names appropriate for the requested column.
*
* @global mixed $data data of currently edited row
* (used to detect whether to choose defaults)
* @global array $cfg PMA configuration
*/
public static function getDefaultFunctionForField(array $field, $insert_mode): string
{
global $cfg, $data, $dbi;
$default_function = '';
// Can we get field class based values?
$current_class = $dbi->types->getTypeClass($field['True_Type']);
if (! empty($current_class) && isset($cfg['DefaultFunctions']['FUNC_' . $current_class])) {
$default_function = $cfg['DefaultFunctions']['FUNC_' . $current_class];
}
// what function defined as default?
// for the first timestamp we don't set the default function
// if there is a default value for the timestamp
// (not including CURRENT_TIMESTAMP)
// and the column does not have the
// ON UPDATE DEFAULT TIMESTAMP attribute.
if (($field['True_Type'] === 'timestamp')
&& $field['first_timestamp']
&& empty($field['Default'])
&& empty($data)
&& $field['Extra'] !== 'on update CURRENT_TIMESTAMP'
&& $field['Null'] === 'NO'
) {
$default_function = $cfg['DefaultFunctions']['first_timestamp'];
}
// For primary keys of type char(36) or varchar(36) UUID if the default
// function
// Only applies to insert mode, as it would silently trash data on updates.
if ($insert_mode
&& $field['Key'] === 'PRI'
&& ($field['Type'] === 'char(36)' || $field['Type'] === 'varchar(36)')
) {
$default_function = $cfg['DefaultFunctions']['FUNC_UUID'];
}
return $default_function;
}
/**
* Creates a dropdown box with MySQL functions for a particular column.
*
* @param array $field Data about the column for which
* to generate the dropdown
* @param bool $insert_mode Whether the operation is 'insert'
* @param array $foreignData Foreign data
*
* @return string An HTML snippet of a dropdown list with function
* names appropriate for the requested column.
*/
public static function getFunctionsForField(array $field, $insert_mode, array $foreignData): string
{
global $dbi;
$default_function = self::getDefaultFunctionForField($field, $insert_mode);
$dropdown_built = [];
// Create the output
$retval = '' . "\n";
// loop on the dropdown array and print all available options for that
// field.
$functions = $dbi->types->getAllFunctions();
foreach ($functions as $function) {
$retval .= '' . "\n";
return $retval;
}
/**
* Renders a single link for the top of the navigation panel
*
* @param string $link The url for the link
* @param bool $showText Whether to show the text or to
* only use it for title attributes
* @param string $text The text to display and use for title attributes
* @param bool $showIcon Whether to show the icon
* @param string $icon The filename of the icon to show
* @param string $linkId Value to use for the ID attribute
* @param bool $disableAjax Whether to disable ajax page loading for this link
* @param string $linkTarget The name of the target frame for the link
* @param array $classes HTML classes to apply
*
* @return string HTML code for one link
*/
public static function getNavigationLink(
$link,
$showText,
$text,
$showIcon,
$icon,
$linkId = '',
$disableAjax = false,
$linkTarget = '',
array $classes = []
): string {
$retval = '';
if ($showIcon) {
$retval .= self::getImage(
$icon,
$text
);
}
if ($showText) {
$retval .= $text;
}
$retval .= '';
if ($showText) {
$retval .= '
';
}
return $retval;
}
/**
* Function to get html for the start row and number of rows panel
*
* @param string $sql_query sql query
*
* @return string html
*
* @throws Throwable
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
public static function getStartAndNumberOfRowsPanel($sql_query): string
{
$template = new Template();
if (isset($_REQUEST['session_max_rows'])) {
$rows = $_REQUEST['session_max_rows'];
} elseif (isset($_SESSION['tmpval']['max_rows'])
&& $_SESSION['tmpval']['max_rows'] !== 'all'
) {
$rows = $_SESSION['tmpval']['max_rows'];
} else {
$rows = (int) $GLOBALS['cfg']['MaxRows'];
$_SESSION['tmpval']['max_rows'] = $rows;
}
if (isset($_REQUEST['pos'])) {
$pos = $_REQUEST['pos'];
} elseif (isset($_SESSION['tmpval']['pos'])) {
$pos = $_SESSION['tmpval']['pos'];
} else {
$number_of_line = (int) $_REQUEST['unlim_num_rows'];
$pos = (ceil($number_of_line / $rows) - 1) * $rows;
$_SESSION['tmpval']['pos'] = $pos;
}
return $template->render(
'start_and_number_of_rows_panel',
[
'pos' => $pos,
'unlim_num_rows' => (int) $_REQUEST['unlim_num_rows'],
'rows' => $rows,
'sql_query' => $sql_query,
]
);
}
/**
* Execute an EXPLAIN query and formats results similar to MySQL command line
* utility.
*
* @param string $sqlQuery EXPLAIN query
*
* @return string query results
*/
private static function generateRowQueryOutput($sqlQuery): string
{
global $dbi;
$ret = '';
$result = $dbi->query($sqlQuery);
if ($result) {
$devider = '+';
$columnNames = '|';
$fieldsMeta = $dbi->getFieldsMeta($result);
foreach ($fieldsMeta as $meta) {
$devider .= '---+';
$columnNames .= ' ' . $meta->name . ' |';
}
$devider .= "\n";
$ret .= $devider . $columnNames . "\n" . $devider;
while ($row = $dbi->fetchRow($result)) {
$values = '|';
foreach ($row as $value) {
if ($value === null) {
$value = 'NULL';
}
$values .= ' ' . $value . ' |';
}
$ret .= $values . "\n";
}
$ret .= $devider;
}
return $ret;
}
/**
* Prepare the message and the query
* usually the message is the result of the query executed
*
* @param Message|string $message the message to display
* @param string $sql_query the query to display
* @param string $type the type (level) of the message
*
* @throws Throwable
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*
* @access public
*/
public static function getMessage(
$message,
$sql_query = null,
$type = 'notice'
): string {
global $cfg, $dbi;
$retval = '';
if ($sql_query === null) {
if (! empty($GLOBALS['display_query'])) {
$sql_query = $GLOBALS['display_query'];
} elseif (! empty($GLOBALS['unparsed_sql'])) {
$sql_query = $GLOBALS['unparsed_sql'];
} elseif (! empty($GLOBALS['sql_query'])) {
$sql_query = $GLOBALS['sql_query'];
} else {
$sql_query = '';
}
}
$render_sql = $cfg['ShowSQL'] == true && ! empty($sql_query) && $sql_query !== ';';
if (isset($GLOBALS['using_bookmark_message'])) {
$retval .= $GLOBALS['using_bookmark_message']->getDisplay();
unset($GLOBALS['using_bookmark_message']);
}
if ($render_sql) {
$retval .= '
' . "\n"
. '$sql = "' . $query_base . '";' . "\n"
. '
';
} elseif ($query_too_big) {
$query_base = '' . "\n" .
htmlspecialchars($query_base, ENT_COMPAT) .
'
';
} 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 = [];
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 = Url::getFromRoute('/table/sql');
} else {
$edit_link = Url::getFromRoute('/database/sql');
}
} else {
$edit_link = Url::getFromRoute('/server/sql');
}
// 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(
Url::getFromRoute('/import', $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(
Url::getFromRoute('/import', $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'),
[],
'_blank'
) . ' ]';
}
}
$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(
Url::getFromRoute('/import', $url_params),
__('Without PHP code')
)
. ' ]';
$php_link .= ' [ '
. self::linkOrButton(
Url::getFromRoute('/import', $url_params),
__('Submit query')
)
. ' ]';
} else {
$php_params = $url_params;
$php_params['show_as_php'] = 1;
$php_link = ' [ '
. self::linkOrButton(
Url::getFromRoute('/import', $php_params),
__('Create PHP code')
)
. ' ]';
}
} else {
$php_link = '';
}
// 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 = Url::getFromRoute('/sql', $url_params);
$refresh_link = ' [ '
. self::linkOrButton($refresh_link, __('Refresh')) . ' ]';
} else {
$refresh_link = '';
}
$retval .= '' . __('Static analysis:') . '
'; $error_msg .= '' . sprintf( __('%d errors were found during analysis.'), count($errors) ) . '
'; $error_msg .= '' . __('SQL query:') . '' . self::showCopyToClipboard( $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 .= MySQLDocumentation::show('SELECT'); } if ($is_modify_link) { $_url_params = [ '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 = (string) preg_replace( "@((\015\012)|(\015)|(\012)){3,}@", "\n\n", (string) $server_msg ); // Adds a link to MySQL documentation. $error_msg .= '' . "\n" . ' ' . __('MySQL said: ') . '' . MySQLDocumentation::show('server-error-reference') . "\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( [ ' ', "\t", ], [ ' ', ' ', ], $server_msg ); // Replace line breaks $server_msg = nl2br($server_msg); $error_msg .= '' . $server_msg . '
' . "\n"
. htmlspecialchars($sqlQuery, ENT_COMPAT) . "\n"
. '
';
}
/**
* This function processes the datatypes supported by the DB,
* as specified in Types->getColumns() and returns an HTML snippet that
* creates a drop-down list.
*
* @param string $selected The value to mark as selected in HTML mode
*/
public static function getSupportedDatatypes($selected): string
{
global $dbi;
// NOTE: the SELECT tag is not included in this snippet.
$retval = '';
foreach ($dbi->types->getColumns() as $key => $value) {
if (is_array($value)) {
$retval .= '';
} elseif ($selected == $value) {
$retval .= sprintf(
'',
$dbi->types->getTypeDescription($value),
$value
);
} else {
$retval .= sprintf(
'',
$dbi->types->getTypeDescription($value),
$value
);
}
}
return $retval;
}
}