Update website
This commit is contained in:
parent
4413528994
commit
1d90fbf296
6865 changed files with 1091082 additions and 0 deletions
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* Handles viewing binary logs
|
||||
*/
|
||||
class BinlogController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* binary log files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $binaryLogs;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
|
||||
$this->binaryLogs = $this->dbi->fetchResult(
|
||||
'SHOW MASTER LOGS',
|
||||
'Log_name'
|
||||
);
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $cfg, $errorUrl;
|
||||
|
||||
$params = [
|
||||
'log' => $_POST['log'] ?? null,
|
||||
'pos' => $_POST['pos'] ?? null,
|
||||
'is_full_query' => $_POST['is_full_query'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$position = ! empty($params['pos']) ? (int) $params['pos'] : 0;
|
||||
|
||||
$urlParams = [];
|
||||
if (isset($params['log']) && array_key_exists($params['log'], $this->binaryLogs)) {
|
||||
$urlParams['log'] = $params['log'];
|
||||
}
|
||||
|
||||
$isFullQuery = false;
|
||||
if (! empty($params['is_full_query'])) {
|
||||
$isFullQuery = true;
|
||||
$urlParams['is_full_query'] = 1;
|
||||
}
|
||||
|
||||
$sqlQuery = $this->getSqlQuery($params['log'] ?? '', $position, (int) $cfg['MaxRows']);
|
||||
$result = $this->dbi->query($sqlQuery);
|
||||
|
||||
$numRows = $result->numRows();
|
||||
|
||||
$previousParams = $urlParams;
|
||||
$fullQueriesParams = $urlParams;
|
||||
$nextParams = $urlParams;
|
||||
if ($position > 0) {
|
||||
$fullQueriesParams['pos'] = $position;
|
||||
if ($position > $cfg['MaxRows']) {
|
||||
$previousParams['pos'] = $position - $cfg['MaxRows'];
|
||||
}
|
||||
}
|
||||
|
||||
$fullQueriesParams['is_full_query'] = 1;
|
||||
if ($isFullQuery) {
|
||||
unset($fullQueriesParams['is_full_query']);
|
||||
}
|
||||
|
||||
if ($numRows >= $cfg['MaxRows']) {
|
||||
$nextParams['pos'] = $position + $cfg['MaxRows'];
|
||||
}
|
||||
|
||||
$values = $result->fetchAllAssoc();
|
||||
|
||||
$this->render('server/binlog/index', [
|
||||
'url_params' => $urlParams,
|
||||
'binary_logs' => $this->binaryLogs,
|
||||
'log' => $params['log'],
|
||||
'sql_message' => Generator::getMessage(Message::success(), $sqlQuery),
|
||||
'values' => $values,
|
||||
'has_previous' => $position > 0,
|
||||
'has_next' => $numRows >= $cfg['MaxRows'],
|
||||
'previous_params' => $previousParams,
|
||||
'full_queries_params' => $fullQueriesParams,
|
||||
'next_params' => $nextParams,
|
||||
'has_icons' => Util::showIcons('TableNavigationLinksMode'),
|
||||
'is_full_query' => $isFullQuery,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $log Binary log file name
|
||||
* @param int $position Position to display
|
||||
* @param int $maxRows Maximum number of rows
|
||||
*/
|
||||
private function getSqlQuery(
|
||||
string $log,
|
||||
int $position,
|
||||
int $maxRows
|
||||
): string {
|
||||
$sqlQuery = 'SHOW BINLOG EVENTS';
|
||||
if (! empty($log)) {
|
||||
$sqlQuery .= ' IN \'' . $log . '\'';
|
||||
}
|
||||
|
||||
$sqlQuery .= ' LIMIT ' . $position . ', ' . $maxRows;
|
||||
|
||||
return $sqlQuery;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\Charsets\Charset;
|
||||
use PhpMyAdmin\Charsets\Collation;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
/**
|
||||
* Handles viewing character sets and collations
|
||||
*/
|
||||
class CollationsController extends AbstractController
|
||||
{
|
||||
/** @var array<string, Charset> */
|
||||
private $charsets;
|
||||
|
||||
/** @var array<string, array<string, Collation>> */
|
||||
private $collations;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/**
|
||||
* @param array<string, Charset>|null $charsets
|
||||
* @param array<string, array<string, Collation>>|null $collations
|
||||
*/
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
DatabaseInterface $dbi,
|
||||
?array $charsets = null,
|
||||
?array $collations = null
|
||||
) {
|
||||
global $cfg;
|
||||
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
|
||||
$this->charsets = $charsets ?? Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
|
||||
$this->collations = $collations ?? Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$charsets = [];
|
||||
foreach ($this->charsets as $charset) {
|
||||
$charsetCollations = [];
|
||||
foreach ($this->collations[$charset->getName()] as $collation) {
|
||||
$charsetCollations[] = [
|
||||
'name' => $collation->getName(),
|
||||
'description' => $collation->getDescription(),
|
||||
'is_default' => $collation->isDefault(),
|
||||
];
|
||||
}
|
||||
|
||||
$charsets[] = [
|
||||
'name' => $charset->getName(),
|
||||
'description' => $charset->getDescription(),
|
||||
'collations' => $charsetCollations,
|
||||
];
|
||||
}
|
||||
|
||||
$this->render('server/collations/index', ['charsets' => $charsets]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Databases;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_key_exists;
|
||||
use function explode;
|
||||
use function mb_strlen;
|
||||
use function mb_strtolower;
|
||||
use function str_contains;
|
||||
|
||||
final class CreateController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $cfg, $db;
|
||||
|
||||
$params = [
|
||||
'new_db' => $_POST['new_db'] ?? null,
|
||||
'db_collation' => $_POST['db_collation'] ?? null,
|
||||
];
|
||||
|
||||
if (! isset($params['new_db']) || mb_strlen($params['new_db']) === 0 || ! $this->response->isAjax()) {
|
||||
$this->response->addJSON(['message' => Message::error()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// lower_case_table_names=1 `DB` becomes `db`
|
||||
if ($this->dbi->getLowerCaseNames() === '1') {
|
||||
$params['new_db'] = mb_strtolower($params['new_db']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and executes the db creation sql query
|
||||
*/
|
||||
$sqlQuery = 'CREATE DATABASE ' . Util::backquote($params['new_db']);
|
||||
if (! empty($params['db_collation'])) {
|
||||
[$databaseCharset] = explode('_', $params['db_collation']);
|
||||
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
|
||||
$collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
|
||||
if (
|
||||
array_key_exists($databaseCharset, $charsets)
|
||||
&& array_key_exists($params['db_collation'], $collations[$databaseCharset])
|
||||
) {
|
||||
$sqlQuery .= ' DEFAULT'
|
||||
. Util::getCharsetQueryPart($params['db_collation']);
|
||||
}
|
||||
}
|
||||
|
||||
$sqlQuery .= ';';
|
||||
|
||||
$result = $this->dbi->tryQuery($sqlQuery);
|
||||
|
||||
if (! $result) {
|
||||
// avoid displaying the not-created db name in header or navi panel
|
||||
$db = '';
|
||||
|
||||
$message = Message::rawError($this->dbi->getError());
|
||||
$json = ['message' => $message];
|
||||
|
||||
$this->response->setRequestStatus(false);
|
||||
} else {
|
||||
$db = $params['new_db'];
|
||||
|
||||
$message = Message::success(__('Database %1$s has been created.'));
|
||||
$message->addParam($params['new_db']);
|
||||
|
||||
$scriptName = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
|
||||
$json = [
|
||||
'message' => $message,
|
||||
'sql_query' => Generator::getMessage('', $sqlQuery, 'success'),
|
||||
'url' => $scriptName . Url::getCommon(
|
||||
['db' => $params['new_db']],
|
||||
! str_contains($scriptName, '?') ? '?' : '&'
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
$this->response->addJSON($json);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Databases;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function _ngettext;
|
||||
use function count;
|
||||
use function is_array;
|
||||
|
||||
final class DestroyController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var RelationCleanup */
|
||||
private $relationCleanup;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
DatabaseInterface $dbi,
|
||||
Transformations $transformations,
|
||||
RelationCleanup $relationCleanup
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
$this->transformations = $transformations;
|
||||
$this->relationCleanup = $relationCleanup;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $selected, $errorUrl, $cfg, $dblist, $reload;
|
||||
|
||||
$selected_dbs = $_POST['selected_dbs'] ?? null;
|
||||
|
||||
if (
|
||||
! $this->response->isAjax()
|
||||
|| (! $this->dbi->isSuperUser() && ! $cfg['AllowUserDropDatabase'])
|
||||
) {
|
||||
$message = Message::error();
|
||||
$json = ['message' => $message];
|
||||
$this->response->setRequestStatus($message->isSuccess());
|
||||
$this->response->addJSON($json);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
! is_array($selected_dbs)
|
||||
|| $selected_dbs === []
|
||||
) {
|
||||
$message = Message::error(__('No databases selected.'));
|
||||
$json = ['message' => $message];
|
||||
$this->response->setRequestStatus($message->isSuccess());
|
||||
$this->response->addJSON($json);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$errorUrl = Url::getFromRoute('/server/databases');
|
||||
$selected = $selected_dbs;
|
||||
$numberOfDatabases = count($selected_dbs);
|
||||
|
||||
foreach ($selected_dbs as $database) {
|
||||
$this->relationCleanup->database($database);
|
||||
$aQuery = 'DROP DATABASE ' . Util::backquote($database);
|
||||
$reload = true;
|
||||
|
||||
$this->dbi->query($aQuery);
|
||||
$this->transformations->clear($database);
|
||||
}
|
||||
|
||||
$dblist->databases->build();
|
||||
|
||||
$message = Message::success(
|
||||
_ngettext(
|
||||
'%1$d database has been dropped successfully.',
|
||||
'%1$d databases have been dropped successfully.',
|
||||
$numberOfDatabases
|
||||
)
|
||||
);
|
||||
$message->addParam($numberOfDatabases);
|
||||
$json = ['message' => $message];
|
||||
$this->response->setRequestStatus($message->isSuccess());
|
||||
$this->response->addJSON($json);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\CheckUserPrivileges;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Query\Utilities;
|
||||
use PhpMyAdmin\ReplicationInfo;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Transformations;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function array_keys;
|
||||
use function array_search;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function mb_strtolower;
|
||||
use function str_contains;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* Handles viewing and creating and deleting databases
|
||||
*/
|
||||
class DatabasesController extends AbstractController
|
||||
{
|
||||
/** @var array array of database details */
|
||||
private $databases = [];
|
||||
|
||||
/** @var int number of databases */
|
||||
private $databaseCount = 0;
|
||||
|
||||
/** @var string sort by column */
|
||||
private $sortBy;
|
||||
|
||||
/** @var string sort order of databases */
|
||||
private $sortOrder;
|
||||
|
||||
/** @var bool whether to show database statistics */
|
||||
private $hasStatistics;
|
||||
|
||||
/** @var int position in list navigation */
|
||||
private $position;
|
||||
|
||||
/** @var Transformations */
|
||||
private $transformations;
|
||||
|
||||
/** @var RelationCleanup */
|
||||
private $relationCleanup;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Transformations $transformations,
|
||||
RelationCleanup $relationCleanup,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->transformations = $transformations;
|
||||
$this->relationCleanup = $relationCleanup;
|
||||
$this->dbi = $dbi;
|
||||
|
||||
$checkUserPrivileges = new CheckUserPrivileges($dbi);
|
||||
$checkUserPrivileges->getPrivileges();
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $cfg, $server, $dblist, $is_create_db_priv;
|
||||
global $db_to_create, $text_dir, $errorUrl;
|
||||
|
||||
$params = [
|
||||
'statistics' => $_REQUEST['statistics'] ?? null,
|
||||
'pos' => $_REQUEST['pos'] ?? null,
|
||||
'sort_by' => $_REQUEST['sort_by'] ?? null,
|
||||
'sort_order' => $_REQUEST['sort_order'] ?? null,
|
||||
];
|
||||
|
||||
$this->addScriptFiles(['server/databases.js']);
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$replicationInfo = new ReplicationInfo($this->dbi);
|
||||
$replicationInfo->load($_POST['primary_connection'] ?? null);
|
||||
|
||||
$primaryInfo = $replicationInfo->getPrimaryInfo();
|
||||
$replicaInfo = $replicationInfo->getReplicaInfo();
|
||||
|
||||
$this->setSortDetails($params['sort_by'], $params['sort_order']);
|
||||
$this->hasStatistics = ! empty($params['statistics']);
|
||||
$this->position = ! empty($params['pos']) ? (int) $params['pos'] : 0;
|
||||
|
||||
/**
|
||||
* Gets the databases list
|
||||
*/
|
||||
if ($server > 0) {
|
||||
$this->databases = $this->dbi->getDatabasesFull(
|
||||
null,
|
||||
$this->hasStatistics,
|
||||
DatabaseInterface::CONNECT_USER,
|
||||
$this->sortBy,
|
||||
$this->sortOrder,
|
||||
$this->position,
|
||||
true
|
||||
);
|
||||
$this->databaseCount = count($dblist->databases);
|
||||
}
|
||||
|
||||
$urlParams = [
|
||||
'statistics' => $this->hasStatistics,
|
||||
'pos' => $this->position,
|
||||
'sort_by' => $this->sortBy,
|
||||
'sort_order' => $this->sortOrder,
|
||||
];
|
||||
|
||||
$databases = $this->getDatabases($primaryInfo, $replicaInfo);
|
||||
|
||||
$charsetsList = [];
|
||||
if ($cfg['ShowCreateDb'] && $is_create_db_priv) {
|
||||
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
|
||||
$collations = Charsets::getCollations($this->dbi, $cfg['Server']['DisableIS']);
|
||||
$serverCollation = $this->dbi->getServerCollation();
|
||||
foreach ($charsets as $charset) {
|
||||
$collationsList = [];
|
||||
foreach ($collations[$charset->getName()] as $collation) {
|
||||
$collationsList[] = [
|
||||
'name' => $collation->getName(),
|
||||
'description' => $collation->getDescription(),
|
||||
'is_selected' => $serverCollation === $collation->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
$charsetsList[] = [
|
||||
'name' => $charset->getName(),
|
||||
'description' => $charset->getDescription(),
|
||||
'collations' => $collationsList,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$headerStatistics = $this->getStatisticsColumns();
|
||||
|
||||
$this->render('server/databases/index', [
|
||||
'is_create_database_shown' => $cfg['ShowCreateDb'],
|
||||
'has_create_database_privileges' => $is_create_db_priv,
|
||||
'has_statistics' => $this->hasStatistics,
|
||||
'database_to_create' => $db_to_create,
|
||||
'databases' => $databases['databases'],
|
||||
'total_statistics' => $databases['total_statistics'],
|
||||
'header_statistics' => $headerStatistics,
|
||||
'charsets' => $charsetsList,
|
||||
'database_count' => $this->databaseCount,
|
||||
'pos' => $this->position,
|
||||
'url_params' => $urlParams,
|
||||
'max_db_list' => $cfg['MaxDbList'],
|
||||
'has_primary_replication' => $primaryInfo['status'],
|
||||
'has_replica_replication' => $replicaInfo['status'],
|
||||
'is_drop_allowed' => $this->dbi->isSuperUser() || $cfg['AllowUserDropDatabase'],
|
||||
'text_dir' => $text_dir,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts parameters sort order and sort by
|
||||
*
|
||||
* @param string|null $sortBy sort by
|
||||
* @param string|null $sortOrder sort order
|
||||
*/
|
||||
private function setSortDetails(?string $sortBy, ?string $sortOrder): void
|
||||
{
|
||||
if (empty($sortBy)) {
|
||||
$this->sortBy = 'SCHEMA_NAME';
|
||||
} else {
|
||||
$sortByAllowList = [
|
||||
'SCHEMA_NAME',
|
||||
'DEFAULT_COLLATION_NAME',
|
||||
'SCHEMA_TABLES',
|
||||
'SCHEMA_TABLE_ROWS',
|
||||
'SCHEMA_DATA_LENGTH',
|
||||
'SCHEMA_INDEX_LENGTH',
|
||||
'SCHEMA_LENGTH',
|
||||
'SCHEMA_DATA_FREE',
|
||||
];
|
||||
$this->sortBy = 'SCHEMA_NAME';
|
||||
if (in_array($sortBy, $sortByAllowList)) {
|
||||
$this->sortBy = $sortBy;
|
||||
}
|
||||
}
|
||||
|
||||
$this->sortOrder = 'asc';
|
||||
if (! isset($sortOrder) || mb_strtolower($sortOrder) !== 'desc') {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sortOrder = 'desc';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $primaryInfo
|
||||
* @param array $replicaInfo
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getDatabases($primaryInfo, $replicaInfo): array
|
||||
{
|
||||
global $cfg;
|
||||
|
||||
$databases = [];
|
||||
$totalStatistics = $this->getStatisticsColumns();
|
||||
foreach ($this->databases as $database) {
|
||||
$replication = [
|
||||
'primary' => ['status' => $primaryInfo['status']],
|
||||
'replica' => ['status' => $replicaInfo['status']],
|
||||
];
|
||||
|
||||
if ($primaryInfo['status']) {
|
||||
$key = array_search($database['SCHEMA_NAME'], $primaryInfo['Ignore_DB']);
|
||||
$replication['primary']['is_replicated'] = false;
|
||||
|
||||
if (strlen((string) $key) === 0) {
|
||||
$key = array_search($database['SCHEMA_NAME'], $primaryInfo['Do_DB']);
|
||||
|
||||
if (strlen((string) $key) > 0 || count($primaryInfo['Do_DB']) === 0) {
|
||||
$replication['primary']['is_replicated'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($replicaInfo['status']) {
|
||||
$key = array_search($database['SCHEMA_NAME'], $replicaInfo['Ignore_DB']);
|
||||
$replication['replica']['is_replicated'] = false;
|
||||
|
||||
if (strlen((string) $key) === 0) {
|
||||
$key = array_search($database['SCHEMA_NAME'], $replicaInfo['Do_DB']);
|
||||
|
||||
if (strlen((string) $key) > 0 || count($replicaInfo['Do_DB']) === 0) {
|
||||
$replication['replica']['is_replicated'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$statistics = $this->getStatisticsColumns();
|
||||
if ($this->hasStatistics) {
|
||||
foreach (array_keys($statistics) as $key) {
|
||||
$statistics[$key]['raw'] = (int) ($database[$key] ?? 0);
|
||||
$totalStatistics[$key]['raw'] += (int) ($database[$key] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
$url = Util::getScriptNameForOption($cfg['DefaultTabDatabase'], 'database');
|
||||
$url .= Url::getCommonRaw(
|
||||
['db' => $database['SCHEMA_NAME']],
|
||||
! str_contains($url, '?') ? '?' : '&'
|
||||
);
|
||||
$databases[$database['SCHEMA_NAME']] = [
|
||||
'name' => $database['SCHEMA_NAME'],
|
||||
'collation' => [],
|
||||
'statistics' => $statistics,
|
||||
'replication' => $replication,
|
||||
'is_system_schema' => Utilities::isSystemSchema($database['SCHEMA_NAME'], true),
|
||||
'is_pmadb' => $database['SCHEMA_NAME'] === ($cfg['Server']['pmadb'] ?? ''),
|
||||
'url' => $url,
|
||||
];
|
||||
$collation = Charsets::findCollationByName(
|
||||
$this->dbi,
|
||||
$cfg['Server']['DisableIS'],
|
||||
$database['DEFAULT_COLLATION_NAME']
|
||||
);
|
||||
if ($collation === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$databases[$database['SCHEMA_NAME']]['collation'] = [
|
||||
'name' => $collation->getName(),
|
||||
'description' => $collation->getDescription(),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'databases' => $databases,
|
||||
'total_statistics' => $totalStatistics,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the statistics columns
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getStatisticsColumns(): array
|
||||
{
|
||||
return [
|
||||
'SCHEMA_TABLES' => [
|
||||
'title' => __('Tables'),
|
||||
'format' => 'number',
|
||||
'raw' => 0,
|
||||
],
|
||||
'SCHEMA_TABLE_ROWS' => [
|
||||
'title' => __('Rows'),
|
||||
'format' => 'number',
|
||||
'raw' => 0,
|
||||
],
|
||||
'SCHEMA_DATA_LENGTH' => [
|
||||
'title' => __('Data'),
|
||||
'format' => 'byte',
|
||||
'raw' => 0,
|
||||
],
|
||||
'SCHEMA_INDEX_LENGTH' => [
|
||||
'title' => __('Indexes'),
|
||||
'format' => 'byte',
|
||||
'raw' => 0,
|
||||
],
|
||||
'SCHEMA_LENGTH' => [
|
||||
'title' => __('Total'),
|
||||
'format' => 'byte',
|
||||
'raw' => 0,
|
||||
],
|
||||
'SCHEMA_DATA_FREE' => [
|
||||
'title' => __('Overhead'),
|
||||
'format' => 'byte',
|
||||
'raw' => 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\StorageEngine;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
/**
|
||||
* Handles viewing storage engine details
|
||||
*/
|
||||
class EnginesController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->render('server/engines/index', [
|
||||
'engines' => StorageEngine::getStorageEngines(),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Export\Options;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function array_merge;
|
||||
|
||||
final class ExportController extends AbstractController
|
||||
{
|
||||
/** @var Options */
|
||||
private $export;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Options $export, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->export = $export;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $sql_query, $num_tables, $unlim_num_rows;
|
||||
global $tmp_select, $select_item, $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$pageSettings = new PageSettings('Export');
|
||||
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
|
||||
$pageSettingsHtml = $pageSettings->getHTML();
|
||||
|
||||
$this->addScriptFiles(['export.js']);
|
||||
|
||||
$select_item = $tmp_select ?? '';
|
||||
$databases = $this->export->getDatabasesForSelectOptions($select_item);
|
||||
|
||||
if (! isset($sql_query)) {
|
||||
$sql_query = '';
|
||||
}
|
||||
|
||||
if (! isset($num_tables)) {
|
||||
$num_tables = 0;
|
||||
}
|
||||
|
||||
if (! isset($unlim_num_rows)) {
|
||||
$unlim_num_rows = 0;
|
||||
}
|
||||
|
||||
$GLOBALS['single_table'] = $_POST['single_table'] ?? $_GET['single_table'] ?? $GLOBALS['single_table'] ?? null;
|
||||
|
||||
$exportList = Plugins::getExport('server', isset($GLOBALS['single_table']));
|
||||
|
||||
if (empty($exportList)) {
|
||||
$this->response->addHTML(Message::error(
|
||||
__('Could not load export plugins, please check your installation!')
|
||||
)->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$options = $this->export->getOptions(
|
||||
'server',
|
||||
$db,
|
||||
$table,
|
||||
$sql_query,
|
||||
$num_tables,
|
||||
$unlim_num_rows,
|
||||
$exportList
|
||||
);
|
||||
|
||||
$this->render('server/export/index', array_merge($options, [
|
||||
'page_settings_error_html' => $pageSettingsErrorHtml,
|
||||
'page_settings_html' => $pageSettingsHtml,
|
||||
'databases' => $databases,
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Charsets;
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Encoding;
|
||||
use PhpMyAdmin\Import;
|
||||
use PhpMyAdmin\Import\Ajax;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Plugins;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
use PhpMyAdmin\Utils\ForeignKey;
|
||||
|
||||
use function __;
|
||||
use function intval;
|
||||
use function is_numeric;
|
||||
|
||||
final class ImportController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $SESSION_KEY, $cfg, $errorUrl;
|
||||
|
||||
$pageSettings = new PageSettings('Import');
|
||||
$pageSettingsErrorHtml = $pageSettings->getErrorHTML();
|
||||
$pageSettingsHtml = $pageSettings->getHTML();
|
||||
|
||||
$this->addScriptFiles(['import.js']);
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
[$SESSION_KEY, $uploadId] = Ajax::uploadProgressSetup();
|
||||
|
||||
$importList = Plugins::getImport('server');
|
||||
|
||||
if (empty($importList)) {
|
||||
$this->response->addHTML(Message::error(__(
|
||||
'Could not load import plugins, please check your installation!'
|
||||
))->getDisplay());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$offset = null;
|
||||
if (isset($_REQUEST['offset']) && is_numeric($_REQUEST['offset'])) {
|
||||
$offset = intval($_REQUEST['offset']);
|
||||
}
|
||||
|
||||
$timeoutPassed = $_REQUEST['timeout_passed'] ?? null;
|
||||
$localImportFile = $_REQUEST['local_import_file'] ?? null;
|
||||
$compressions = Import::getCompressions();
|
||||
|
||||
$charsets = Charsets::getCharsets($this->dbi, $cfg['Server']['DisableIS']);
|
||||
|
||||
$idKey = $_SESSION[$SESSION_KEY]['handler']::getIdKey();
|
||||
$hiddenInputs = [
|
||||
$idKey => $uploadId,
|
||||
'import_type' => 'server',
|
||||
];
|
||||
|
||||
$default = isset($_GET['format']) ? (string) $_GET['format'] : Plugins::getDefault('Import', 'format');
|
||||
$choice = Plugins::getChoice($importList, $default);
|
||||
$options = Plugins::getOptions('Import', $importList);
|
||||
$skipQueriesDefault = Plugins::getDefault('Import', 'skip_queries');
|
||||
$isAllowInterruptChecked = Plugins::checkboxCheck('Import', 'allow_interrupt');
|
||||
$maxUploadSize = (int) $GLOBALS['config']->get('max_upload_size');
|
||||
|
||||
$this->render('server/import/index', [
|
||||
'page_settings_error_html' => $pageSettingsErrorHtml,
|
||||
'page_settings_html' => $pageSettingsHtml,
|
||||
'upload_id' => $uploadId,
|
||||
'handler' => $_SESSION[$SESSION_KEY]['handler'],
|
||||
'hidden_inputs' => $hiddenInputs,
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
'max_upload_size' => $maxUploadSize,
|
||||
'formatted_maximum_upload_size' => Util::getFormattedMaximumUploadSize($maxUploadSize),
|
||||
'plugins_choice' => $choice,
|
||||
'options' => $options,
|
||||
'skip_queries_default' => $skipQueriesDefault,
|
||||
'is_allow_interrupt_checked' => $isAllowInterruptChecked,
|
||||
'local_import_file' => $localImportFile,
|
||||
'is_upload' => $GLOBALS['config']->get('enable_upload'),
|
||||
'upload_dir' => $cfg['UploadDir'] ?? null,
|
||||
'timeout_passed_global' => $GLOBALS['timeout_passed'] ?? null,
|
||||
'compressions' => $compressions,
|
||||
'is_encoding_supported' => Encoding::isSupported(),
|
||||
'encodings' => Encoding::listEncodings(),
|
||||
'import_charset' => $cfg['Import']['charset'] ?? null,
|
||||
'timeout_passed' => $timeoutPassed,
|
||||
'offset' => $offset,
|
||||
'can_convert_kanji' => Encoding::canConvertKanji(),
|
||||
'charsets' => $charsets,
|
||||
'is_foreign_key_check' => ForeignKey::isCheckEnabled(),
|
||||
'user_upload_dir' => Util::userDir((string) ($cfg['UploadDir'] ?? '')),
|
||||
'local_files' => Import::getLocalFiles($importList),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Plugins;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function array_keys;
|
||||
use function ksort;
|
||||
use function mb_strtolower;
|
||||
use function preg_replace;
|
||||
|
||||
/**
|
||||
* Handles viewing server plugin details
|
||||
*/
|
||||
class PluginsController extends AbstractController
|
||||
{
|
||||
/** @var Plugins */
|
||||
private $plugins;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Plugins $plugins,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->plugins = $plugins;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['vendor/jquery/jquery.tablesorter.js', 'server/plugins.js']);
|
||||
|
||||
$plugins = [];
|
||||
$serverPlugins = $this->plugins->getAll();
|
||||
foreach ($serverPlugins as $plugin) {
|
||||
$plugins[$plugin->getType()][] = $plugin->toArray();
|
||||
}
|
||||
|
||||
ksort($plugins);
|
||||
|
||||
$cleanTypes = [];
|
||||
foreach (array_keys($plugins) as $type) {
|
||||
$cleanTypes[$type] = preg_replace(
|
||||
'/[^a-z]/',
|
||||
'',
|
||||
mb_strtolower($type)
|
||||
);
|
||||
}
|
||||
|
||||
$this->render('server/plugins/index', [
|
||||
'plugins' => $plugins,
|
||||
'clean_types' => $cleanTypes,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Privileges;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Privileges\AccountLocking;
|
||||
use PhpMyAdmin\Template;
|
||||
use Throwable;
|
||||
|
||||
use function __;
|
||||
|
||||
final class AccountLockController extends AbstractController
|
||||
{
|
||||
/** @var AccountLocking */
|
||||
private $model;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, AccountLocking $accountLocking)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->model = $accountLocking;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$this->response->setAjax(true);
|
||||
|
||||
/** @var string $userName */
|
||||
$userName = $request->getParsedBodyParam('username');
|
||||
/** @var string $hostName */
|
||||
$hostName = $request->getParsedBodyParam('hostname');
|
||||
|
||||
try {
|
||||
$this->model->lock($userName, $hostName);
|
||||
} catch (Throwable $exception) {
|
||||
$this->response->setHttpResponseCode(400);
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON(['message' => Message::error($exception->getMessage())]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$message = Message::success(__('The account %s@%s has been successfully locked.'));
|
||||
$message->addParam($userName);
|
||||
$message->addParam($hostName);
|
||||
$this->response->addJSON(['message' => $message]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Privileges;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Privileges\AccountLocking;
|
||||
use PhpMyAdmin\Template;
|
||||
use Throwable;
|
||||
|
||||
use function __;
|
||||
|
||||
final class AccountUnlockController extends AbstractController
|
||||
{
|
||||
/** @var AccountLocking */
|
||||
private $model;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, AccountLocking $accountLocking)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->model = $accountLocking;
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request): void
|
||||
{
|
||||
$this->response->setAjax(true);
|
||||
|
||||
/** @var string $userName */
|
||||
$userName = $request->getParsedBodyParam('username');
|
||||
/** @var string $hostName */
|
||||
$hostName = $request->getParsedBodyParam('hostname');
|
||||
|
||||
try {
|
||||
$this->model->unlock($userName, $hostName);
|
||||
} catch (Throwable $exception) {
|
||||
$this->response->setHttpResponseCode(400);
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->addJSON(['message' => Message::error($exception->getMessage())]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$message = Message::success(__('The account %s@%s has been successfully unlocked.'));
|
||||
$message->addParam($userName);
|
||||
$message->addParam($hostName);
|
||||
$this->response->addJSON(['message' => $message]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,475 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\CheckUserPrivileges;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\Controllers\Database\PrivilegesController as DatabaseController;
|
||||
use PhpMyAdmin\Controllers\Table\PrivilegesController as TableController;
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Plugins;
|
||||
use PhpMyAdmin\Server\Privileges;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function header;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use function str_replace;
|
||||
use function urlencode;
|
||||
|
||||
/**
|
||||
* Server privileges and users manipulations.
|
||||
*/
|
||||
class PrivilegesController extends AbstractController
|
||||
{
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $db, $table, $errorUrl, $message, $text_dir, $post_patterns;
|
||||
global $username, $hostname, $dbname, $tablename, $routinename, $db_and_table, $dbname_is_wildcard;
|
||||
global $queries, $password, $ret_message, $ret_queries, $queries_for_display, $sql_query, $_add_user_error;
|
||||
global $itemType, $tables, $num_tables, $total_num_tables, $sub_part;
|
||||
global $tooltip_truename, $tooltip_aliasname, $pos, $title, $export, $grants, $one_grant, $url_dbname;
|
||||
|
||||
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
|
||||
$checkUserPrivileges->getPrivileges();
|
||||
|
||||
$relationParameters = $this->relation->getRelationParameters();
|
||||
|
||||
$this->addScriptFiles(['server/privileges.js', 'vendor/zxcvbn-ts.js']);
|
||||
|
||||
$relationCleanup = new RelationCleanup($this->dbi, $this->relation);
|
||||
$serverPrivileges = new Privileges(
|
||||
$this->template,
|
||||
$this->dbi,
|
||||
$this->relation,
|
||||
$relationCleanup,
|
||||
new Plugins($this->dbi)
|
||||
);
|
||||
|
||||
$databaseController = new DatabaseController(
|
||||
$this->response,
|
||||
$this->template,
|
||||
$db,
|
||||
$serverPrivileges,
|
||||
$this->dbi
|
||||
);
|
||||
|
||||
$tableController = new TableController(
|
||||
$this->response,
|
||||
$this->template,
|
||||
$db,
|
||||
$table,
|
||||
$serverPrivileges,
|
||||
$this->dbi
|
||||
);
|
||||
|
||||
if (
|
||||
(isset($_GET['viewing_mode'])
|
||||
&& $_GET['viewing_mode'] === 'server')
|
||||
&& $relationParameters->configurableMenusFeature !== null
|
||||
) {
|
||||
$this->response->addHTML('<div class="container-fluid">');
|
||||
$this->render('server/privileges/subnav', [
|
||||
'active' => 'privileges',
|
||||
'is_super_user' => $this->dbi->isSuperUser(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets globals from $_POST patterns, for privileges and max_* vars
|
||||
*/
|
||||
$post_patterns = [
|
||||
'/_priv$/i',
|
||||
'/^max_/i',
|
||||
];
|
||||
|
||||
Core::setPostAsGlobal($post_patterns);
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$_add_user_error = false;
|
||||
/**
|
||||
* Get DB information: username, hostname, dbname,
|
||||
* tablename, db_and_table, dbname_is_wildcard
|
||||
*/
|
||||
[
|
||||
$username,
|
||||
$hostname,
|
||||
$dbname,
|
||||
$tablename,
|
||||
$routinename,
|
||||
$db_and_table,
|
||||
$dbname_is_wildcard,
|
||||
] = $serverPrivileges->getDataForDBInfo();
|
||||
|
||||
/**
|
||||
* Checks if the user is allowed to do what they try to...
|
||||
*/
|
||||
$isGrantUser = $this->dbi->isGrantUser();
|
||||
$isCreateUser = $this->dbi->isCreateUser();
|
||||
|
||||
if (! $this->dbi->isSuperUser() && ! $isGrantUser && ! $isCreateUser) {
|
||||
$this->render('server/sub_page_header', [
|
||||
'type' => 'privileges',
|
||||
'is_image' => false,
|
||||
]);
|
||||
$this->response->addHTML(
|
||||
Message::error(__('No Privileges'))
|
||||
->getDisplay()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $isGrantUser && ! $isCreateUser) {
|
||||
$this->response->addHTML(Message::notice(
|
||||
__('You do not have the privileges to administrate the users!')
|
||||
)->getDisplay());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user is using "Change Login Information / Copy User" dialog
|
||||
* only to update the password
|
||||
*/
|
||||
if (
|
||||
isset($_POST['change_copy']) && $username == $_POST['old_username']
|
||||
&& $hostname == $_POST['old_hostname']
|
||||
) {
|
||||
$this->response->addHTML(
|
||||
Message::error(
|
||||
__(
|
||||
"Username and hostname didn't change. "
|
||||
. 'If you only want to change the password, '
|
||||
. "'Change password' tab should be used."
|
||||
)
|
||||
)->getDisplay()
|
||||
);
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes / copies a user, part I
|
||||
*/
|
||||
[$queries, $password] = $serverPrivileges->getDataForChangeOrCopyUser();
|
||||
|
||||
/**
|
||||
* Adds a user
|
||||
* (Changes / copies a user, part II)
|
||||
*/
|
||||
[
|
||||
$ret_message,
|
||||
$ret_queries,
|
||||
$queries_for_display,
|
||||
$sql_query,
|
||||
$_add_user_error,
|
||||
] = $serverPrivileges->addUser(
|
||||
$dbname ?? null,
|
||||
$username ?? '',
|
||||
$hostname ?? '',
|
||||
$password ?? null,
|
||||
$relationParameters->configurableMenusFeature !== null
|
||||
);
|
||||
//update the old variables
|
||||
if (isset($ret_queries)) {
|
||||
$queries = $ret_queries;
|
||||
unset($ret_queries);
|
||||
}
|
||||
|
||||
if (isset($ret_message)) {
|
||||
$message = $ret_message;
|
||||
unset($ret_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes / copies a user, part III
|
||||
*/
|
||||
if (isset($_POST['change_copy']) && $username !== null && $hostname !== null) {
|
||||
$queries = $serverPrivileges->getDbSpecificPrivsQueriesForChangeOrCopyUser($queries, $username, $hostname);
|
||||
}
|
||||
|
||||
$itemType = '';
|
||||
if (! empty($routinename) && is_string($dbname)) {
|
||||
$itemType = $serverPrivileges->getRoutineType($dbname, $routinename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates privileges
|
||||
*/
|
||||
if (! empty($_POST['update_privs'])) {
|
||||
if (is_array($dbname)) {
|
||||
foreach ($dbname as $key => $db_name) {
|
||||
[$sql_query[$key], $message] = $serverPrivileges->updatePrivileges(
|
||||
($username ?? ''),
|
||||
($hostname ?? ''),
|
||||
($tablename ?? ($routinename ?? '')),
|
||||
($db_name ?? ''),
|
||||
$itemType
|
||||
);
|
||||
}
|
||||
|
||||
$sql_query = implode("\n", $sql_query);
|
||||
} else {
|
||||
[$sql_query, $message] = $serverPrivileges->updatePrivileges(
|
||||
($username ?? ''),
|
||||
($hostname ?? ''),
|
||||
($tablename ?? ($routinename ?? '')),
|
||||
($dbname ?? ''),
|
||||
$itemType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign users to user groups
|
||||
*/
|
||||
if (
|
||||
! empty($_POST['changeUserGroup']) && $relationParameters->configurableMenusFeature !== null
|
||||
&& $this->dbi->isSuperUser() && $this->dbi->isCreateUser()
|
||||
) {
|
||||
$serverPrivileges->setUserGroup($username ?? '', $_POST['userGroup']);
|
||||
$message = Message::success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes Privileges
|
||||
*/
|
||||
if (isset($_POST['revokeall'])) {
|
||||
[$message, $sql_query] = $serverPrivileges->getMessageAndSqlQueryForPrivilegesRevoke(
|
||||
(is_string($dbname) ? $dbname : ''),
|
||||
($tablename ?? ($routinename ?? '')),
|
||||
$username ?? '',
|
||||
$hostname ?? '',
|
||||
$itemType
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the password
|
||||
*/
|
||||
if (isset($_POST['change_pw'])) {
|
||||
$message = $serverPrivileges->updatePassword($errorUrl, $username ?? '', $hostname ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes users
|
||||
* (Changes / copies a user, part IV)
|
||||
*/
|
||||
if (isset($_POST['delete']) || (isset($_POST['change_copy']) && $_POST['mode'] < 4)) {
|
||||
$queries = $serverPrivileges->getDataForDeleteUsers($queries);
|
||||
if (empty($_POST['change_copy'])) {
|
||||
[$sql_query, $message] = $serverPrivileges->deleteUser($queries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes / copies a user, part V
|
||||
*/
|
||||
if (isset($_POST['change_copy'])) {
|
||||
$queries = $serverPrivileges->getDataForQueries($queries, $queries_for_display);
|
||||
$message = Message::success();
|
||||
$sql_query = implode("\n", $queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the privilege tables into memory
|
||||
*/
|
||||
$message_ret = $serverPrivileges->updateMessageForReload();
|
||||
if ($message_ret !== null) {
|
||||
$message = $message_ret;
|
||||
unset($message_ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are in an Ajax request for Create User/Edit User/Revoke User/
|
||||
* Flush Privileges, show $message and return.
|
||||
*/
|
||||
if (
|
||||
$this->response->isAjax()
|
||||
&& empty($_REQUEST['ajax_page_request'])
|
||||
&& ! isset($_GET['export'])
|
||||
&& (! isset($_POST['submit_mult']) || $_POST['submit_mult'] !== 'export')
|
||||
&& ((! isset($_GET['initial']) || $_GET['initial'] === '')
|
||||
|| (isset($_POST['delete']) && $_POST['delete'] === __('Go')))
|
||||
&& ! isset($_GET['showall'])
|
||||
) {
|
||||
$extra_data = $serverPrivileges->getExtraDataForAjaxBehavior(
|
||||
($password ?? ''),
|
||||
($sql_query ?? ''),
|
||||
($hostname ?? ''),
|
||||
($username ?? '')
|
||||
);
|
||||
|
||||
if (! empty($message) && $message instanceof Message) {
|
||||
$this->response->setRequestStatus($message->isSuccess());
|
||||
$this->response->addJSON('message', $message);
|
||||
$this->response->addJSON($extra_data);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the links
|
||||
*/
|
||||
if (isset($_GET['viewing_mode']) && $_GET['viewing_mode'] === 'db') {
|
||||
$db = $_REQUEST['db'] = $_GET['checkprivsdb'];
|
||||
|
||||
// Gets the database structure
|
||||
$sub_part = '_structure';
|
||||
ob_start();
|
||||
|
||||
[
|
||||
$tables,
|
||||
$num_tables,
|
||||
$total_num_tables,
|
||||
$sub_part,,,
|
||||
$tooltip_truename,
|
||||
$tooltip_aliasname,
|
||||
$pos,
|
||||
] = Util::getDbInfo($db, $sub_part);
|
||||
|
||||
$content = ob_get_clean();
|
||||
$this->response->addHTML($content . "\n");
|
||||
} elseif (! empty($GLOBALS['message'])) {
|
||||
$this->response->addHTML(Generator::getMessage($GLOBALS['message']));
|
||||
unset($GLOBALS['message']);
|
||||
}
|
||||
|
||||
// export user definition
|
||||
if (isset($_GET['export']) || (isset($_POST['submit_mult']) && $_POST['submit_mult'] === 'export')) {
|
||||
[$title, $export] = $serverPrivileges->getListForExportUserDefinition($username ?? '', $hostname ?? '');
|
||||
|
||||
unset($username, $hostname, $grants, $one_grant);
|
||||
|
||||
if ($this->response->isAjax()) {
|
||||
$this->response->addJSON('message', $export);
|
||||
$this->response->addJSON('title', $title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addHTML('<h2>' . $title . '</h2>' . $export);
|
||||
}
|
||||
|
||||
// Show back the form if an error occurred
|
||||
if (isset($_GET['adduser']) || $_add_user_error === true) {
|
||||
// Add user
|
||||
$this->response->addHTML(
|
||||
$serverPrivileges->getHtmlForAddUser(Util::escapeMysqlWildcards(is_string($dbname) ? $dbname : ''))
|
||||
);
|
||||
} elseif (isset($_GET['checkprivsdb']) && is_string($_GET['checkprivsdb'])) {
|
||||
if (isset($_GET['checkprivstable']) && is_string($_GET['checkprivstable'])) {
|
||||
$this->response->addHTML($tableController([
|
||||
'checkprivsdb' => $_GET['checkprivsdb'],
|
||||
'checkprivstable' => $_GET['checkprivstable'],
|
||||
]));
|
||||
$this->render('export_modal');
|
||||
} elseif ($this->response->isAjax() === true && empty($_REQUEST['ajax_page_request'])) {
|
||||
$message = Message::success(__('User has been added.'));
|
||||
$this->response->addJSON('message', $message);
|
||||
|
||||
return;
|
||||
} else {
|
||||
$this->response->addHTML($databaseController(['checkprivsdb' => $_GET['checkprivsdb']]));
|
||||
$this->render('export_modal');
|
||||
}
|
||||
} else {
|
||||
if (isset($dbname) && ! is_array($dbname)) {
|
||||
$url_dbname = urlencode(
|
||||
str_replace(
|
||||
[
|
||||
'\_',
|
||||
'\%',
|
||||
],
|
||||
[
|
||||
'_',
|
||||
'%',
|
||||
],
|
||||
$dbname
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (! isset($username)) {
|
||||
// No username is given --> display the overview
|
||||
$this->response->addHTML(
|
||||
$serverPrivileges->getHtmlForUserOverview($text_dir)
|
||||
);
|
||||
} elseif (! empty($routinename)) {
|
||||
$this->response->addHTML(
|
||||
$serverPrivileges->getHtmlForRoutineSpecificPrivileges(
|
||||
$username,
|
||||
$hostname ?? '',
|
||||
is_string($dbname) ? $dbname : '',
|
||||
$routinename,
|
||||
Util::escapeMysqlWildcards($url_dbname ?? '')
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// A user was selected -> display the user's properties
|
||||
// In an Ajax request, prevent cached values from showing
|
||||
if ($this->response->isAjax()) {
|
||||
header('Cache-Control: no-cache');
|
||||
}
|
||||
|
||||
$this->response->addHTML(
|
||||
$serverPrivileges->getHtmlForUserProperties(
|
||||
$dbname_is_wildcard,
|
||||
Util::escapeMysqlWildcards($url_dbname ?? ''),
|
||||
$username,
|
||||
$hostname ?? '',
|
||||
$dbname ?? '',
|
||||
$tablename ?? ''
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
! isset($_GET['viewing_mode'])
|
||||
|| $_GET['viewing_mode'] !== 'server'
|
||||
|| $relationParameters->configurableMenusFeature === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addHTML('</div>');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
/**
|
||||
* Server replications
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ReplicationGui;
|
||||
use PhpMyAdmin\ReplicationInfo;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Server replications
|
||||
*/
|
||||
class ReplicationController extends AbstractController
|
||||
{
|
||||
/** @var ReplicationGui */
|
||||
private $replicationGui;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
ReplicationGui $replicationGui,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->replicationGui = $replicationGui;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $urlParams, $errorUrl;
|
||||
|
||||
$params = [
|
||||
'url_params' => $_POST['url_params'] ?? null,
|
||||
'primary_configure' => $_POST['primary_configure'] ?? null,
|
||||
'replica_configure' => $_POST['replica_configure'] ?? null,
|
||||
'repl_clear_scr' => $_POST['repl_clear_scr'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$replicationInfo = new ReplicationInfo($this->dbi);
|
||||
$replicationInfo->load($_POST['primary_connection'] ?? null);
|
||||
|
||||
$primaryInfo = $replicationInfo->getPrimaryInfo();
|
||||
$replicaInfo = $replicationInfo->getReplicaInfo();
|
||||
|
||||
$this->addScriptFiles(['server/privileges.js', 'replication.js', 'vendor/zxcvbn-ts.js']);
|
||||
|
||||
if (isset($params['url_params']) && is_array($params['url_params'])) {
|
||||
$urlParams = $params['url_params'];
|
||||
}
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->replicationGui->handleControlRequest();
|
||||
}
|
||||
|
||||
$errorMessages = $this->replicationGui->getHtmlForErrorMessage();
|
||||
|
||||
if ($primaryInfo['status']) {
|
||||
$primaryReplicationHtml = $this->replicationGui->getHtmlForPrimaryReplication();
|
||||
}
|
||||
|
||||
if (isset($params['primary_configure'])) {
|
||||
$primaryConfigurationHtml = $this->replicationGui->getHtmlForPrimaryConfiguration();
|
||||
} else {
|
||||
if (! isset($params['repl_clear_scr'])) {
|
||||
$replicaConfigurationHtml = $this->replicationGui->getHtmlForReplicaConfiguration(
|
||||
$replicaInfo['status'],
|
||||
$replicationInfo->getReplicaStatus()
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($params['replica_configure'])) {
|
||||
$changePrimaryHtml = $this->replicationGui->getHtmlForReplicationChangePrimary('replica_changeprimary');
|
||||
}
|
||||
}
|
||||
|
||||
$this->render('server/replication/index', [
|
||||
'url_params' => $urlParams,
|
||||
'is_super_user' => $this->dbi->isSuperUser(),
|
||||
'error_messages' => $errorMessages,
|
||||
'is_primary' => $primaryInfo['status'],
|
||||
'primary_configure' => $params['primary_configure'],
|
||||
'replica_configure' => $params['replica_configure'],
|
||||
'clear_screen' => $params['repl_clear_scr'],
|
||||
'primary_replication_html' => $primaryReplicationHtml ?? '',
|
||||
'primary_configuration_html' => $primaryConfigurationHtml ?? '',
|
||||
'replica_configuration_html' => $replicaConfigurationHtml ?? '',
|
||||
'change_primary_html' => $changePrimaryHtml ?? '',
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\StorageEngine;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
/**
|
||||
* Displays details about a given Storage Engine.
|
||||
*/
|
||||
final class ShowEngineController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @psalm-param array{engine: string, page?: string} $params
|
||||
*/
|
||||
public function __invoke(ServerRequest $request, array $params): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$page = $params['page'] ?? '';
|
||||
|
||||
$engine = [];
|
||||
if (StorageEngine::isValid($params['engine'])) {
|
||||
$storageEngine = StorageEngine::getEngine($params['engine']);
|
||||
$engine = [
|
||||
'engine' => $params['engine'],
|
||||
'title' => $storageEngine->getTitle(),
|
||||
'help_page' => $storageEngine->getMysqlHelpPage(),
|
||||
'comment' => $storageEngine->getComment(),
|
||||
'info_pages' => $storageEngine->getInfoPages(),
|
||||
'support' => $storageEngine->getSupportInformationMessage(),
|
||||
'variables' => $storageEngine->getHtmlVariables(),
|
||||
'page' => ! empty($page) ? $storageEngine->getPage($page) : '',
|
||||
];
|
||||
}
|
||||
|
||||
$this->render('server/engines/show', [
|
||||
'engine' => $engine,
|
||||
'page' => $page,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Config\PageSettings;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\SqlQueryForm;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
/**
|
||||
* Server SQL executor
|
||||
*/
|
||||
class SqlController extends AbstractController
|
||||
{
|
||||
/** @var SqlQueryForm */
|
||||
private $sqlQueryForm;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
SqlQueryForm $sqlQueryForm,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->sqlQueryForm = $sqlQueryForm;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$this->addScriptFiles(['makegrid.js', 'vendor/jquery/jquery.uitablefilter.js', 'sql.js']);
|
||||
|
||||
$pageSettings = new PageSettings('Sql');
|
||||
$this->response->addHTML($pageSettings->getErrorHTML());
|
||||
$this->response->addHTML($pageSettings->getHTML());
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->response->addHTML($this->sqlQueryForm->getHtml('', ''));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController as Controller;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
abstract class AbstractController extends Controller
|
||||
{
|
||||
/** @var Data */
|
||||
protected $data;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\Advisor;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
/**
|
||||
* Displays the advisor feature
|
||||
*/
|
||||
class AdvisorController extends AbstractController
|
||||
{
|
||||
/** @var Advisor */
|
||||
private $advisor;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data, Advisor $advisor)
|
||||
{
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->advisor = $advisor;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$data = [];
|
||||
if ($this->data->dataLoaded) {
|
||||
$data = $this->advisor->run();
|
||||
}
|
||||
|
||||
$this->render('server/status/advisor/index', ['data' => $data]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Monitor;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
final class ChartingDataController extends AbstractController
|
||||
{
|
||||
/** @var Monitor */
|
||||
private $monitor;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
Monitor $monitor,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->monitor = $monitor;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = ['requiredData' => $_POST['requiredData'] ?? null];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON([
|
||||
'message' => $this->monitor->getJsonForChartingData(
|
||||
$params['requiredData'] ?? ''
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Monitor;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
final class GeneralLogController extends AbstractController
|
||||
{
|
||||
/** @var Monitor */
|
||||
private $monitor;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
Monitor $monitor,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->monitor = $monitor;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = [
|
||||
'time_start' => $_POST['time_start'] ?? null,
|
||||
'time_end' => $_POST['time_end'] ?? null,
|
||||
'limitTypes' => $_POST['limitTypes'] ?? null,
|
||||
'removeVariables' => $_POST['removeVariables'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->monitor->getJsonForLogDataTypeGeneral(
|
||||
(int) $params['time_start'],
|
||||
(int) $params['time_end'],
|
||||
(bool) $params['limitTypes'],
|
||||
(bool) $params['removeVariables']
|
||||
);
|
||||
|
||||
if ($data === null) {
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON(['message' => $data]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Monitor;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
final class LogVarsController extends AbstractController
|
||||
{
|
||||
/** @var Monitor */
|
||||
private $monitor;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
Monitor $monitor,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->monitor = $monitor;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = [
|
||||
'varName' => $_POST['varName'] ?? null,
|
||||
'varValue' => $_POST['varValue'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON([
|
||||
'message' => $this->monitor->getJsonForLoggingVars(
|
||||
$params['varName'],
|
||||
$params['varValue']
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Monitor;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
final class QueryAnalyzerController extends AbstractController
|
||||
{
|
||||
/** @var Monitor */
|
||||
private $monitor;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
Monitor $monitor,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->monitor = $monitor;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = [
|
||||
'database' => $_POST['database'] ?? null,
|
||||
'query' => $_POST['query'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON([
|
||||
'message' => $this->monitor->getJsonForQueryAnalyzer(
|
||||
$params['database'] ?? '',
|
||||
$params['query'] ?? ''
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Monitor;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Monitor;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
final class SlowLogController extends AbstractController
|
||||
{
|
||||
/** @var Monitor */
|
||||
private $monitor;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
Monitor $monitor,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->monitor = $monitor;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = [
|
||||
'time_start' => $_POST['time_start'] ?? null,
|
||||
'time_end' => $_POST['time_end'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->monitor->getJsonForLogDataTypeSlow(
|
||||
(int) $params['time_start'],
|
||||
(int) $params['time_end']
|
||||
);
|
||||
|
||||
if ($data === null) {
|
||||
$this->response->setRequestStatus(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addJSON(['message' => $data]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\SysInfo\SysInfo;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function is_numeric;
|
||||
use function microtime;
|
||||
|
||||
class MonitorController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->addScriptFiles([
|
||||
'vendor/jquery/jquery.tablesorter.js',
|
||||
'jquery.sortable-table.js',
|
||||
'vendor/jqplot/jquery.jqplot.js',
|
||||
'vendor/jqplot/plugins/jqplot.pieRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.enhancedPieLegendRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.canvasTextRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.dateAxisRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.highlighter.js',
|
||||
'vendor/jqplot/plugins/jqplot.cursor.js',
|
||||
'jqplot/plugins/jqplot.byteFormatter.js',
|
||||
'server/status/monitor.js',
|
||||
'server/status/sorter.js',
|
||||
'chart.js',// Needed by createProfilingChart in server/status/monitor.js
|
||||
]);
|
||||
|
||||
$form = [
|
||||
'server_time' => (int) (microtime(true) * 1000),
|
||||
'server_os' => SysInfo::getOs(),
|
||||
'is_superuser' => $this->dbi->isSuperUser(),
|
||||
'server_db_isLocal' => $this->data->dbIsLocal,
|
||||
];
|
||||
|
||||
$javascriptVariableNames = [];
|
||||
foreach ($this->data->status as $name => $value) {
|
||||
if (! is_numeric($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$javascriptVariableNames[] = $name;
|
||||
}
|
||||
|
||||
$this->render('server/status/monitor/index', [
|
||||
'javascript_variable_names' => $javascriptVariableNames,
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Processes;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
use function __;
|
||||
|
||||
final class KillController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params Request parameters
|
||||
*/
|
||||
public function __invoke(ServerRequest $request, array $params): void
|
||||
{
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$kill = (int) $params['id'];
|
||||
$query = $this->dbi->getKillQuery($kill);
|
||||
|
||||
if ($this->dbi->tryQuery($query)) {
|
||||
$message = Message::success(
|
||||
__('Thread %s was successfully killed.')
|
||||
);
|
||||
$this->response->setRequestStatus(true);
|
||||
} else {
|
||||
$message = Message::error(
|
||||
__(
|
||||
'phpMyAdmin was unable to kill thread %s. It probably has already been closed.'
|
||||
)
|
||||
);
|
||||
$this->response->setRequestStatus(false);
|
||||
}
|
||||
|
||||
$message->addParam($kill);
|
||||
|
||||
$this->response->addJSON(['message' => $message]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status\Processes;
|
||||
|
||||
use PhpMyAdmin\Controllers\Server\Status\AbstractController;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Processes;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
final class RefreshController extends AbstractController
|
||||
{
|
||||
/** @var Processes */
|
||||
private $processes;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data, Processes $processes)
|
||||
{
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->processes = $processes;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$params = [
|
||||
'showExecuting' => $_POST['showExecuting'] ?? null,
|
||||
'full' => $_POST['full'] ?? null,
|
||||
'column_name' => $_POST['column_name'] ?? null,
|
||||
'order_by_field' => $_POST['order_by_field'] ?? null,
|
||||
'sort_order' => $_POST['sort_order'] ?? null,
|
||||
];
|
||||
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->render('server/status/processes/list', $this->processes->getList($params));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Server\Status\Processes;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
class ProcessesController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
/** @var Processes */
|
||||
private $processes;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
DatabaseInterface $dbi,
|
||||
Processes $processes
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->dbi = $dbi;
|
||||
$this->processes = $processes;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = [
|
||||
'showExecuting' => $_POST['showExecuting'] ?? null,
|
||||
'full' => $_POST['full'] ?? null,
|
||||
'column_name' => $_POST['column_name'] ?? null,
|
||||
'order_by_field' => $_POST['order_by_field'] ?? null,
|
||||
'sort_order' => $_POST['sort_order'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['server/status/processes.js']);
|
||||
|
||||
$isChecked = false;
|
||||
if (! empty($params['showExecuting'])) {
|
||||
$isChecked = true;
|
||||
}
|
||||
|
||||
$urlParams = [
|
||||
'ajax_request' => true,
|
||||
'full' => $params['full'] ?? '',
|
||||
'column_name' => $params['column_name'] ?? '',
|
||||
'order_by_field' => $params['order_by_field'] ?? '',
|
||||
'sort_order' => $params['sort_order'] ?? '',
|
||||
];
|
||||
|
||||
$listHtml = $this->template->render('server/status/processes/list', $this->processes->getList($params));
|
||||
|
||||
$this->render('server/status/processes/index', [
|
||||
'url_params' => $urlParams,
|
||||
'is_checked' => $isChecked,
|
||||
'server_process_list' => $listHtml,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
/**
|
||||
* Displays query statistics for the server
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function array_sum;
|
||||
use function arsort;
|
||||
use function count;
|
||||
use function str_replace;
|
||||
|
||||
class QueriesController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->addScriptFiles([
|
||||
'chart.js',
|
||||
'vendor/jqplot/jquery.jqplot.js',
|
||||
'vendor/jqplot/plugins/jqplot.pieRenderer.js',
|
||||
'vendor/jqplot/plugins/jqplot.highlighter.js',
|
||||
'vendor/jqplot/plugins/jqplot.enhancedPieLegendRenderer.js',
|
||||
'vendor/jquery/jquery.tablesorter.js',
|
||||
'server/status/sorter.js',
|
||||
'server/status/queries.js',
|
||||
]);
|
||||
|
||||
if ($this->data->dataLoaded) {
|
||||
$hourFactor = 3600 / $this->data->status['Uptime'];
|
||||
$usedQueries = $this->data->usedQueries;
|
||||
$totalQueries = array_sum($usedQueries);
|
||||
|
||||
$stats = [
|
||||
'total' => $totalQueries,
|
||||
'per_hour' => $totalQueries * $hourFactor,
|
||||
'per_minute' => $totalQueries * 60 / $this->data->status['Uptime'],
|
||||
'per_second' => $totalQueries / $this->data->status['Uptime'],
|
||||
];
|
||||
|
||||
// reverse sort by value to show most used statements first
|
||||
arsort($usedQueries);
|
||||
|
||||
$chart = [];
|
||||
$querySum = array_sum($usedQueries);
|
||||
$otherSum = 0;
|
||||
$queries = [];
|
||||
foreach ($usedQueries as $key => $value) {
|
||||
// For the percentage column, use Questions - Connections, because
|
||||
// the number of connections is not an item of the Query types
|
||||
// but is included in Questions. Then the total of the percentages is 100.
|
||||
$name = str_replace(['Com_', '_'], ['', ' '], $key);
|
||||
// Group together values that make out less than 2% into "Other", but only
|
||||
// if we have more than 6 fractions already
|
||||
if ($value < $querySum * 0.02 && count($chart) > 6) {
|
||||
$otherSum += $value;
|
||||
} else {
|
||||
$chart[$name] = $value;
|
||||
}
|
||||
|
||||
$queries[$key] = [
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'per_hour' => $value * $hourFactor,
|
||||
'percentage' => $value * 100 / $totalQueries,
|
||||
];
|
||||
}
|
||||
|
||||
if ($otherSum > 0) {
|
||||
$chart[__('Other')] = $otherSum;
|
||||
}
|
||||
}
|
||||
|
||||
$this->render('server/status/queries/index', [
|
||||
'is_data_loaded' => $this->data->dataLoaded,
|
||||
'stats' => $stats ?? null,
|
||||
'queries' => $queries ?? [],
|
||||
'chart' => $chart ?? [],
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ReplicationGui;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function implode;
|
||||
|
||||
/**
|
||||
* Object the server status page: processes, connections and traffic.
|
||||
*/
|
||||
class StatusController extends AbstractController
|
||||
{
|
||||
/** @var ReplicationGui */
|
||||
private $replicationGui;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Data $data,
|
||||
ReplicationGui $replicationGui,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->replicationGui = $replicationGui;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$replicationInfo = $this->data->getReplicationInfo();
|
||||
$primaryInfo = $replicationInfo->getPrimaryInfo();
|
||||
$replicaInfo = $replicationInfo->getReplicaInfo();
|
||||
|
||||
$traffic = [];
|
||||
$connections = [];
|
||||
$replication = '';
|
||||
if ($this->data->dataLoaded) {
|
||||
// In some case the data was reported not to exist, check it for all keys
|
||||
if (isset($this->data->status['Bytes_received'], $this->data->status['Bytes_sent'])) {
|
||||
/** @var string[] $bytes */
|
||||
$bytes = Util::formatByteDown(
|
||||
$this->data->status['Bytes_received'] + $this->data->status['Bytes_sent'],
|
||||
3,
|
||||
1
|
||||
);
|
||||
$networkTraffic = implode(' ', $bytes);
|
||||
}
|
||||
|
||||
if (isset($this->data->status['Uptime'])) {
|
||||
$uptime = Util::timespanFormat($this->data->status['Uptime']);
|
||||
}
|
||||
|
||||
$startTime = Util::localisedDate($this->getStartTime());
|
||||
|
||||
$traffic = $this->getTrafficInfo();
|
||||
|
||||
$connections = $this->getConnectionsInfo();
|
||||
|
||||
if ($primaryInfo['status']) {
|
||||
$replication .= $this->replicationGui->getHtmlForReplicationStatusTable('primary');
|
||||
}
|
||||
|
||||
if ($replicaInfo['status']) {
|
||||
$replication .= $this->replicationGui->getHtmlForReplicationStatusTable('replica');
|
||||
}
|
||||
}
|
||||
|
||||
$this->render('server/status/status/index', [
|
||||
'is_data_loaded' => $this->data->dataLoaded,
|
||||
'network_traffic' => $networkTraffic ?? null,
|
||||
'uptime' => $uptime ?? null,
|
||||
'start_time' => $startTime ?? null,
|
||||
'traffic' => $traffic,
|
||||
'connections' => $connections,
|
||||
'is_primary' => $primaryInfo['status'],
|
||||
'is_replica' => $replicaInfo['status'],
|
||||
'replication' => $replication,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getStartTime(): int
|
||||
{
|
||||
return (int) $this->dbi->fetchValue('SELECT UNIX_TIMESTAMP() - ' . $this->data->status['Uptime']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getTrafficInfo(): array
|
||||
{
|
||||
$hourFactor = 3600 / $this->data->status['Uptime'];
|
||||
|
||||
/** @var string[] $bytesReceived */
|
||||
$bytesReceived = Util::formatByteDown($this->data->status['Bytes_received'], 3, 1);
|
||||
/** @var string[] $bytesReceivedPerHour */
|
||||
$bytesReceivedPerHour = Util::formatByteDown($this->data->status['Bytes_received'] * $hourFactor, 3, 1);
|
||||
/** @var string[] $bytesSent */
|
||||
$bytesSent = Util::formatByteDown($this->data->status['Bytes_sent'], 3, 1);
|
||||
/** @var string[] $bytesSentPerHour */
|
||||
$bytesSentPerHour = Util::formatByteDown($this->data->status['Bytes_sent'] * $hourFactor, 3, 1);
|
||||
/** @var string[] $bytesTotal */
|
||||
$bytesTotal = Util::formatByteDown(
|
||||
$this->data->status['Bytes_received'] + $this->data->status['Bytes_sent'],
|
||||
3,
|
||||
1
|
||||
);
|
||||
/** @var string[] $bytesTotalPerHour */
|
||||
$bytesTotalPerHour = Util::formatByteDown(
|
||||
($this->data->status['Bytes_received'] + $this->data->status['Bytes_sent']) * $hourFactor,
|
||||
3,
|
||||
1
|
||||
);
|
||||
|
||||
return [
|
||||
[
|
||||
'name' => __('Received'),
|
||||
'number' => implode(' ', $bytesReceived),
|
||||
'per_hour' => implode(' ', $bytesReceivedPerHour),
|
||||
],
|
||||
[
|
||||
'name' => __('Sent'),
|
||||
'number' => implode(' ', $bytesSent),
|
||||
'per_hour' => implode(' ', $bytesSentPerHour),
|
||||
],
|
||||
[
|
||||
'name' => __('Total'),
|
||||
'number' => implode(' ', $bytesTotal),
|
||||
'per_hour' => implode(' ', $bytesTotalPerHour),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getConnectionsInfo(): array
|
||||
{
|
||||
$hourFactor = 3600 / $this->data->status['Uptime'];
|
||||
|
||||
$failedAttemptsPercentage = '---';
|
||||
$abortedPercentage = '---';
|
||||
if ($this->data->status['Connections'] > 0) {
|
||||
$failedAttemptsPercentage = Util::formatNumber(
|
||||
$this->data->status['Aborted_connects'] * 100 / $this->data->status['Connections'],
|
||||
0,
|
||||
2,
|
||||
true
|
||||
) . '%';
|
||||
|
||||
$abortedPercentage = Util::formatNumber(
|
||||
$this->data->status['Aborted_clients'] * 100 / $this->data->status['Connections'],
|
||||
0,
|
||||
2,
|
||||
true
|
||||
) . '%';
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'name' => __('Max. concurrent connections'),
|
||||
'number' => Util::formatNumber($this->data->status['Max_used_connections'], 0),
|
||||
'per_hour' => '---',
|
||||
'percentage' => '---',
|
||||
],
|
||||
[
|
||||
'name' => __('Failed attempts'),
|
||||
'number' => Util::formatNumber($this->data->status['Aborted_connects'], 4, 1, true),
|
||||
'per_hour' => Util::formatNumber($this->data->status['Aborted_connects'] * $hourFactor, 4, 2, true),
|
||||
'percentage' => $failedAttemptsPercentage,
|
||||
],
|
||||
[
|
||||
'name' => __('Aborted'),
|
||||
'number' => Util::formatNumber($this->data->status['Aborted_clients'], 4, 1, true),
|
||||
'per_hour' => Util::formatNumber($this->data->status['Aborted_clients'] * $hourFactor, 4, 2, true),
|
||||
'percentage' => $abortedPercentage,
|
||||
],
|
||||
[
|
||||
'name' => __('Total'),
|
||||
'number' => Util::formatNumber($this->data->status['Connections'], 4, 0),
|
||||
'per_hour' => Util::formatNumber($this->data->status['Connections'] * $hourFactor, 4, 2),
|
||||
'percentage' => Util::formatNumber(100, 0, 2) . '%',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,520 @@
|
|||
<?php
|
||||
/**
|
||||
* Displays a list of server status variables
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Status;
|
||||
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Server\Status\Data;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function __;
|
||||
use function in_array;
|
||||
use function is_numeric;
|
||||
use function str_contains;
|
||||
|
||||
class VariablesController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, Data $data, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template, $data);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = [
|
||||
'flush' => $_POST['flush'] ?? null,
|
||||
'filterAlert' => $_POST['filterAlert'] ?? null,
|
||||
'filterText' => $_POST['filterText'] ?? null,
|
||||
'filterCategory' => $_POST['filterCategory'] ?? null,
|
||||
'dontFormat' => $_POST['dontFormat'] ?? null,
|
||||
];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$this->addScriptFiles([
|
||||
'server/status/variables.js',
|
||||
'vendor/jquery/jquery.tablesorter.js',
|
||||
'server/status/sorter.js',
|
||||
]);
|
||||
|
||||
if (isset($params['flush'])) {
|
||||
$this->flush($params['flush']);
|
||||
}
|
||||
|
||||
if ($this->data->dataLoaded) {
|
||||
$categories = [];
|
||||
foreach ($this->data->sections as $sectionId => $sectionName) {
|
||||
if (! isset($this->data->sectionUsed[$sectionId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$categories[$sectionId] = [
|
||||
'id' => $sectionId,
|
||||
'name' => $sectionName,
|
||||
'is_selected' => false,
|
||||
];
|
||||
if (empty($params['filterCategory']) || $params['filterCategory'] !== $sectionId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$categories[$sectionId]['is_selected'] = true;
|
||||
}
|
||||
|
||||
$links = [];
|
||||
foreach ($this->data->links as $sectionName => $sectionLinks) {
|
||||
$links[$sectionName] = [
|
||||
'name' => 'status_' . $sectionName,
|
||||
'links' => $sectionLinks,
|
||||
];
|
||||
}
|
||||
|
||||
$descriptions = $this->getDescriptions();
|
||||
$alerts = $this->getAlerts();
|
||||
|
||||
$variables = [];
|
||||
foreach ($this->data->status as $name => $value) {
|
||||
$variables[$name] = [
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'is_numeric' => is_numeric($value),
|
||||
'class' => $this->data->allocationMap[$name] ?? null,
|
||||
'doc' => '',
|
||||
'has_alert' => false,
|
||||
'is_alert' => false,
|
||||
'description' => $descriptions[$name] ?? '',
|
||||
'description_doc' => [],
|
||||
];
|
||||
|
||||
// Fields containing % are calculated,
|
||||
// they can not be described in MySQL documentation
|
||||
if (! str_contains($name, '%')) {
|
||||
$variables[$name]['doc'] = Generator::linkToVarDocumentation(
|
||||
$name,
|
||||
$this->dbi->isMariaDB()
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($alerts[$name])) {
|
||||
$variables[$name]['has_alert'] = true;
|
||||
if ($value > $alerts[$name]) {
|
||||
$variables[$name]['is_alert'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($this->data->links[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->data->links[$name] as $linkName => $linkUrl) {
|
||||
$variables[$name]['description_doc'][] = [
|
||||
'name' => $linkName,
|
||||
'url' => $linkUrl,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->render('server/status/variables/index', [
|
||||
'is_data_loaded' => $this->data->dataLoaded,
|
||||
'filter_text' => ! empty($params['filterText']) ? $params['filterText'] : '',
|
||||
'is_only_alerts' => ! empty($params['filterAlert']),
|
||||
'is_not_formatted' => ! empty($params['dontFormat']),
|
||||
'categories' => $categories ?? [],
|
||||
'links' => $links ?? [],
|
||||
'variables' => $variables ?? [],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush status variables if requested
|
||||
*
|
||||
* @param string $flush Variable name
|
||||
*/
|
||||
private function flush(string $flush): void
|
||||
{
|
||||
$flushCommands = [
|
||||
'STATUS',
|
||||
'TABLES',
|
||||
'QUERY CACHE',
|
||||
];
|
||||
|
||||
if (! in_array($flush, $flushCommands)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dbi->query('FLUSH ' . $flush . ';');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getAlerts(): array
|
||||
{
|
||||
// name => max value before alert
|
||||
return [
|
||||
// lower is better
|
||||
// variable => max value
|
||||
'Aborted_clients' => 0,
|
||||
'Aborted_connects' => 0,
|
||||
|
||||
'Binlog_cache_disk_use' => 0,
|
||||
|
||||
'Created_tmp_disk_tables' => 0,
|
||||
|
||||
'Handler_read_rnd' => 0,
|
||||
'Handler_read_rnd_next' => 0,
|
||||
|
||||
'Innodb_buffer_pool_pages_dirty' => 0,
|
||||
'Innodb_buffer_pool_reads' => 0,
|
||||
'Innodb_buffer_pool_wait_free' => 0,
|
||||
'Innodb_log_waits' => 0,
|
||||
'Innodb_row_lock_time_avg' => 10, // ms
|
||||
'Innodb_row_lock_time_max' => 50, // ms
|
||||
'Innodb_row_lock_waits' => 0,
|
||||
|
||||
'Slow_queries' => 0,
|
||||
'Delayed_errors' => 0,
|
||||
'Select_full_join' => 0,
|
||||
'Select_range_check' => 0,
|
||||
'Sort_merge_passes' => 0,
|
||||
'Opened_tables' => 0,
|
||||
'Table_locks_waited' => 0,
|
||||
'Qcache_lowmem_prunes' => 0,
|
||||
|
||||
'Qcache_free_blocks' => isset($this->data->status['Qcache_total_blocks'])
|
||||
? $this->data->status['Qcache_total_blocks'] / 5
|
||||
: 0,
|
||||
'Slow_launch_threads' => 0,
|
||||
|
||||
// depends on Key_read_requests
|
||||
// normally lower then 1:0.01
|
||||
'Key_reads' => isset($this->data->status['Key_read_requests'])
|
||||
? 0.01 * $this->data->status['Key_read_requests'] : 0,
|
||||
// depends on Key_write_requests
|
||||
// normally nearly 1:1
|
||||
'Key_writes' => isset($this->data->status['Key_write_requests'])
|
||||
? 0.9 * $this->data->status['Key_write_requests'] : 0,
|
||||
|
||||
'Key_buffer_fraction' => 0.5,
|
||||
|
||||
// alert if more than 95% of thread cache is in use
|
||||
'Threads_cached' => isset($this->data->variables['thread_cache_size'])
|
||||
? 0.95 * $this->data->variables['thread_cache_size'] : 0,
|
||||
|
||||
// higher is better
|
||||
// variable => min value
|
||||
//'Handler read key' => '> ',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of variable descriptions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getDescriptions(): array
|
||||
{
|
||||
/**
|
||||
* Messages are built using the message name
|
||||
*/
|
||||
return [
|
||||
'Aborted_clients' => __(
|
||||
'The number of connections that were aborted because the client died'
|
||||
. ' without closing the connection properly.'
|
||||
),
|
||||
'Aborted_connects' => __('The number of failed attempts to connect to the MySQL server.'),
|
||||
'Binlog_cache_disk_use' => __(
|
||||
'The number of transactions that used the temporary binary log cache'
|
||||
. ' but that exceeded the value of binlog_cache_size and used a'
|
||||
. ' temporary file to store statements from the transaction.'
|
||||
),
|
||||
'Binlog_cache_use' => __('The number of transactions that used the temporary binary log cache.'),
|
||||
'Connections' => __('The number of connection attempts (successful or not) to the MySQL server.'),
|
||||
'Created_tmp_disk_tables' => __(
|
||||
'The number of temporary tables on disk created automatically by'
|
||||
. ' the server while executing statements. If'
|
||||
. ' Created_tmp_disk_tables is big, you may want to increase the'
|
||||
. ' tmp_table_size value to cause temporary tables to be'
|
||||
. ' memory-based instead of disk-based.'
|
||||
),
|
||||
'Created_tmp_files' => __('How many temporary files mysqld has created.'),
|
||||
'Created_tmp_tables' => __(
|
||||
'The number of in-memory temporary tables created automatically'
|
||||
. ' by the server while executing statements.'
|
||||
),
|
||||
'Delayed_errors' => __(
|
||||
'The number of rows written with INSERT DELAYED for which some'
|
||||
. ' error occurred (probably duplicate key).'
|
||||
),
|
||||
'Delayed_insert_threads' => __(
|
||||
'The number of INSERT DELAYED handler threads in use. Every'
|
||||
. ' different table on which one uses INSERT DELAYED gets'
|
||||
. ' its own thread.'
|
||||
),
|
||||
'Delayed_writes' => __('The number of INSERT DELAYED rows written.'),
|
||||
'Flush_commands' => __('The number of executed FLUSH statements.'),
|
||||
'Handler_commit' => __('The number of internal COMMIT statements.'),
|
||||
'Handler_delete' => __('The number of times a row was deleted from a table.'),
|
||||
'Handler_discover' => __(
|
||||
'The MySQL server can ask the NDB Cluster storage engine if it'
|
||||
. ' knows about a table with a given name. This is called discovery.'
|
||||
. ' Handler_discover indicates the number of time tables have been'
|
||||
. ' discovered.'
|
||||
),
|
||||
'Handler_read_first' => __(
|
||||
'The number of times the first entry was read from an index. If this'
|
||||
. ' is high, it suggests that the server is doing a lot of full'
|
||||
. ' index scans; for example, SELECT col1 FROM foo, assuming that'
|
||||
. ' col1 is indexed.'
|
||||
),
|
||||
'Handler_read_key' => __(
|
||||
'The number of requests to read a row based on a key. If this is'
|
||||
. ' high, it is a good indication that your queries and tables'
|
||||
. ' are properly indexed.'
|
||||
),
|
||||
'Handler_read_next' => __(
|
||||
'The number of requests to read the next row in key order. This is'
|
||||
. ' incremented if you are querying an index column with a range'
|
||||
. ' constraint or if you are doing an index scan.'
|
||||
),
|
||||
'Handler_read_prev' => __(
|
||||
'The number of requests to read the previous row in key order.'
|
||||
. ' This read method is mainly used to optimize ORDER BY … DESC.'
|
||||
),
|
||||
'Handler_read_rnd' => __(
|
||||
'The number of requests to read a row based on a fixed position.'
|
||||
. ' This is high if you are doing a lot of queries that require'
|
||||
. ' sorting of the result. You probably have a lot of queries that'
|
||||
. ' require MySQL to scan whole tables or you have joins that'
|
||||
. ' don\'t use keys properly.'
|
||||
),
|
||||
'Handler_read_rnd_next' => __(
|
||||
'The number of requests to read the next row in the data file.'
|
||||
. ' This is high if you are doing a lot of table scans. Generally'
|
||||
. ' this suggests that your tables are not properly indexed or that'
|
||||
. ' your queries are not written to take advantage of the indexes'
|
||||
. ' you have.'
|
||||
),
|
||||
'Handler_rollback' => __('The number of internal ROLLBACK statements.'),
|
||||
'Handler_update' => __('The number of requests to update a row in a table.'),
|
||||
'Handler_write' => __('The number of requests to insert a row in a table.'),
|
||||
'Innodb_buffer_pool_pages_data' => __('The number of pages containing data (dirty or clean).'),
|
||||
'Innodb_buffer_pool_pages_dirty' => __('The number of pages currently dirty.'),
|
||||
'Innodb_buffer_pool_pages_flushed' => __(
|
||||
'The number of buffer pool pages that have been requested to be flushed.'
|
||||
),
|
||||
'Innodb_buffer_pool_pages_free' => __('The number of free pages.'),
|
||||
'Innodb_buffer_pool_pages_latched' => __(
|
||||
'The number of latched pages in InnoDB buffer pool. These are pages'
|
||||
. ' currently being read or written or that can\'t be flushed or'
|
||||
. ' removed for some other reason.'
|
||||
),
|
||||
'Innodb_buffer_pool_pages_misc' => __(
|
||||
'The number of pages busy because they have been allocated for'
|
||||
. ' administrative overhead such as row locks or the adaptive'
|
||||
. ' hash index. This value can also be calculated as'
|
||||
. ' Innodb_buffer_pool_pages_total - Innodb_buffer_pool_pages_free'
|
||||
. ' - Innodb_buffer_pool_pages_data.'
|
||||
),
|
||||
'Innodb_buffer_pool_pages_total' => __('Total size of buffer pool, in pages.'),
|
||||
'Innodb_buffer_pool_read_ahead_rnd' => __(
|
||||
'The number of "random" read-aheads InnoDB initiated. This happens'
|
||||
. ' when a query is to scan a large portion of a table but in'
|
||||
. ' random order.'
|
||||
),
|
||||
'Innodb_buffer_pool_read_ahead_seq' => __(
|
||||
'The number of sequential read-aheads InnoDB initiated. This'
|
||||
. ' happens when InnoDB does a sequential full table scan.'
|
||||
),
|
||||
'Innodb_buffer_pool_read_requests' => __('The number of logical read requests InnoDB has done.'),
|
||||
'Innodb_buffer_pool_reads' => __(
|
||||
'The number of logical reads that InnoDB could not satisfy'
|
||||
. ' from buffer pool and had to do a single-page read.'
|
||||
),
|
||||
'Innodb_buffer_pool_wait_free' => __(
|
||||
'Normally, writes to the InnoDB buffer pool happen in the'
|
||||
. ' background. However, if it\'s necessary to read or create a page'
|
||||
. ' and no clean pages are available, it\'s necessary to wait for'
|
||||
. ' pages to be flushed first. This counter counts instances of'
|
||||
. ' these waits. If the buffer pool size was set properly, this'
|
||||
. ' value should be small.'
|
||||
),
|
||||
'Innodb_buffer_pool_write_requests' => __('The number writes done to the InnoDB buffer pool.'),
|
||||
'Innodb_data_fsyncs' => __('The number of fsync() operations so far.'),
|
||||
'Innodb_data_pending_fsyncs' => __('The current number of pending fsync() operations.'),
|
||||
'Innodb_data_pending_reads' => __('The current number of pending reads.'),
|
||||
'Innodb_data_pending_writes' => __('The current number of pending writes.'),
|
||||
'Innodb_data_read' => __('The amount of data read so far, in bytes.'),
|
||||
'Innodb_data_reads' => __('The total number of data reads.'),
|
||||
'Innodb_data_writes' => __('The total number of data writes.'),
|
||||
'Innodb_data_written' => __('The amount of data written so far, in bytes.'),
|
||||
'Innodb_dblwr_pages_written' => __(
|
||||
'The number of pages that have been written for doublewrite operations.'
|
||||
),
|
||||
'Innodb_dblwr_writes' => __('The number of doublewrite operations that have been performed.'),
|
||||
'Innodb_log_waits' => __(
|
||||
'The number of waits we had because log buffer was too small and'
|
||||
. ' we had to wait for it to be flushed before continuing.'
|
||||
),
|
||||
'Innodb_log_write_requests' => __('The number of log write requests.'),
|
||||
'Innodb_log_writes' => __('The number of physical writes to the log file.'),
|
||||
'Innodb_os_log_fsyncs' => __('The number of fsync() writes done to the log file.'),
|
||||
'Innodb_os_log_pending_fsyncs' => __('The number of pending log file fsyncs.'),
|
||||
'Innodb_os_log_pending_writes' => __('Pending log file writes.'),
|
||||
'Innodb_os_log_written' => __('The number of bytes written to the log file.'),
|
||||
'Innodb_pages_created' => __('The number of pages created.'),
|
||||
'Innodb_page_size' => __(
|
||||
'The compiled-in InnoDB page size (default 16KB). Many values are'
|
||||
. ' counted in pages; the page size allows them to be easily'
|
||||
. ' converted to bytes.'
|
||||
),
|
||||
'Innodb_pages_read' => __('The number of pages read.'),
|
||||
'Innodb_pages_written' => __('The number of pages written.'),
|
||||
'Innodb_row_lock_current_waits' => __('The number of row locks currently being waited for.'),
|
||||
'Innodb_row_lock_time_avg' => __('The average time to acquire a row lock, in milliseconds.'),
|
||||
'Innodb_row_lock_time' => __('The total time spent in acquiring row locks, in milliseconds.'),
|
||||
'Innodb_row_lock_time_max' => __('The maximum time to acquire a row lock, in milliseconds.'),
|
||||
'Innodb_row_lock_waits' => __('The number of times a row lock had to be waited for.'),
|
||||
'Innodb_rows_deleted' => __('The number of rows deleted from InnoDB tables.'),
|
||||
'Innodb_rows_inserted' => __('The number of rows inserted in InnoDB tables.'),
|
||||
'Innodb_rows_read' => __('The number of rows read from InnoDB tables.'),
|
||||
'Innodb_rows_updated' => __('The number of rows updated in InnoDB tables.'),
|
||||
'Key_blocks_not_flushed' => __(
|
||||
'The number of key blocks in the key cache that have changed but'
|
||||
. ' haven\'t yet been flushed to disk. It used to be known as'
|
||||
. ' Not_flushed_key_blocks.'
|
||||
),
|
||||
'Key_blocks_unused' => __(
|
||||
'The number of unused blocks in the key cache. You can use this'
|
||||
. ' value to determine how much of the key cache is in use.'
|
||||
),
|
||||
'Key_blocks_used' => __(
|
||||
'The number of used blocks in the key cache. This value is a'
|
||||
. ' high-water mark that indicates the maximum number of blocks'
|
||||
. ' that have ever been in use at one time.'
|
||||
),
|
||||
'Key_buffer_fraction_%' => __('Percentage of used key cache (calculated value)'),
|
||||
'Key_read_requests' => __('The number of requests to read a key block from the cache.'),
|
||||
'Key_reads' => __(
|
||||
'The number of physical reads of a key block from disk. If Key_reads'
|
||||
. ' is big, then your key_buffer_size value is probably too small.'
|
||||
. ' The cache miss rate can be calculated as'
|
||||
. ' Key_reads/Key_read_requests.'
|
||||
),
|
||||
'Key_read_ratio_%' => __(
|
||||
'Key cache miss calculated as rate of physical reads compared to read requests (calculated value)'
|
||||
),
|
||||
'Key_write_requests' => __('The number of requests to write a key block to the cache.'),
|
||||
'Key_writes' => __('The number of physical writes of a key block to disk.'),
|
||||
'Key_write_ratio_%' => __('Percentage of physical writes compared to write requests (calculated value)'),
|
||||
'Last_query_cost' => __(
|
||||
'The total cost of the last compiled query as computed by the query'
|
||||
. ' optimizer. Useful for comparing the cost of different query'
|
||||
. ' plans for the same query. The default value of 0 means that'
|
||||
. ' no query has been compiled yet.'
|
||||
),
|
||||
'Max_used_connections' => __(
|
||||
'The maximum number of connections that have been in use simultaneously since the server started.'
|
||||
),
|
||||
'Not_flushed_delayed_rows' => __('The number of rows waiting to be written in INSERT DELAYED queues.'),
|
||||
'Opened_tables' => __(
|
||||
'The number of tables that have been opened. If opened tables is'
|
||||
. ' big, your table_open_cache value is probably too small.'
|
||||
),
|
||||
'Open_files' => __('The number of files that are open.'),
|
||||
'Open_streams' => __('The number of streams that are open (used mainly for logging).'),
|
||||
'Open_tables' => __('The number of tables that are open.'),
|
||||
'Qcache_free_blocks' => __(
|
||||
'The number of free memory blocks in query cache. High numbers can'
|
||||
. ' indicate fragmentation issues, which may be solved by issuing'
|
||||
. ' a FLUSH QUERY CACHE statement.'
|
||||
),
|
||||
'Qcache_free_memory' => __('The amount of free memory for query cache.'),
|
||||
'Qcache_hits' => __('The number of cache hits.'),
|
||||
'Qcache_inserts' => __('The number of queries added to the cache.'),
|
||||
'Qcache_lowmem_prunes' => __(
|
||||
'The number of queries that have been removed from the cache to'
|
||||
. ' free up memory for caching new queries. This information can'
|
||||
. ' help you tune the query cache size. The query cache uses a'
|
||||
. ' least recently used (LRU) strategy to decide which queries'
|
||||
. ' to remove from the cache.'
|
||||
),
|
||||
'Qcache_not_cached' => __(
|
||||
'The number of non-cached queries (not cachable, or not cached due to the query_cache_type setting).'
|
||||
),
|
||||
'Qcache_queries_in_cache' => __('The number of queries registered in the cache.'),
|
||||
'Qcache_total_blocks' => __('The total number of blocks in the query cache.'),
|
||||
'Rpl_status' => __('The status of failsafe replication (not yet implemented).'),
|
||||
'Select_full_join' => __(
|
||||
'The number of joins that do not use indexes. If this value is'
|
||||
. ' not 0, you should carefully check the indexes of your tables.'
|
||||
),
|
||||
'Select_full_range_join' => __('The number of joins that used a range search on a reference table.'),
|
||||
'Select_range_check' => __(
|
||||
'The number of joins without keys that check for key usage after'
|
||||
. ' each row. (If this is not 0, you should carefully check the'
|
||||
. ' indexes of your tables.)'
|
||||
),
|
||||
'Select_range' => __(
|
||||
'The number of joins that used ranges on the first table. (It\'s'
|
||||
. ' normally not critical even if this is big.)'
|
||||
),
|
||||
'Select_scan' => __('The number of joins that did a full scan of the first table.'),
|
||||
'Slave_open_temp_tables' => __('The number of temporary tables currently open by the replica SQL thread.'),
|
||||
'Slave_retried_transactions' => __(
|
||||
'Total (since startup) number of times the replication replica SQL thread has retried transactions.'
|
||||
),
|
||||
'Slave_running' => __('This is ON if this server is a replica that is connected to a primary.'),
|
||||
'Slow_launch_threads' => __(
|
||||
'The number of threads that have taken more than slow_launch_time seconds to create.'
|
||||
),
|
||||
'Slow_queries' => __('The number of queries that have taken more than long_query_time seconds.'),
|
||||
'Sort_merge_passes' => __(
|
||||
'The number of merge passes the sort algorithm has had to do.'
|
||||
. ' If this value is large, you should consider increasing the'
|
||||
. ' value of the sort_buffer_size system variable.'
|
||||
),
|
||||
'Sort_range' => __('The number of sorts that were done with ranges.'),
|
||||
'Sort_rows' => __('The number of sorted rows.'),
|
||||
'Sort_scan' => __('The number of sorts that were done by scanning the table.'),
|
||||
'Table_locks_immediate' => __('The number of times that a table lock was acquired immediately.'),
|
||||
'Table_locks_waited' => __(
|
||||
'The number of times that a table lock could not be acquired'
|
||||
. ' immediately and a wait was needed. If this is high, and you have'
|
||||
. ' performance problems, you should first optimize your queries,'
|
||||
. ' and then either split your table or tables or use replication.'
|
||||
),
|
||||
'Threads_cached' => __(
|
||||
'The number of threads in the thread cache. The cache hit rate can'
|
||||
. ' be calculated as Threads_created/Connections. If this value is'
|
||||
. ' red you should raise your thread_cache_size.'
|
||||
),
|
||||
'Threads_connected' => __('The number of currently open connections.'),
|
||||
'Threads_created' => __(
|
||||
'The number of threads created to handle connections. If'
|
||||
. ' Threads_created is big, you may want to increase the'
|
||||
. ' thread_cache_size value. (Normally this doesn\'t give a notable'
|
||||
. ' performance improvement if you have a good thread'
|
||||
. ' implementation.)'
|
||||
),
|
||||
'Threads_cache_hitrate_%' => __('Thread cache hit rate (calculated value)'),
|
||||
'Threads_running' => __('The number of threads that are not sleeping.'),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\UserGroups;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
|
||||
use function __;
|
||||
|
||||
/**
|
||||
* Displays the 'User groups' sub page under 'Users' page.
|
||||
*/
|
||||
class UserGroupsController extends AbstractController
|
||||
{
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$configurableMenusFeature = $this->relation->getRelationParameters()->configurableMenusFeature;
|
||||
if ($configurableMenusFeature === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addScriptFiles(['server/user_groups.js']);
|
||||
|
||||
/**
|
||||
* Only allowed to superuser
|
||||
*/
|
||||
if (! $this->dbi->isSuperUser()) {
|
||||
$this->response->addHTML(
|
||||
Message::error(__('No Privileges'))->getDisplay()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->addHTML('<div class="container-fluid">');
|
||||
$this->render('server/privileges/subnav', [
|
||||
'active' => 'user-groups',
|
||||
'is_super_user' => $this->dbi->isSuperUser(),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Delete user group
|
||||
*/
|
||||
if (! empty($_POST['deleteUserGroup'])) {
|
||||
UserGroups::delete($configurableMenusFeature, $_POST['userGroup']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new user group
|
||||
*/
|
||||
if (! empty($_POST['addUserGroupSubmit'])) {
|
||||
UserGroups::edit($configurableMenusFeature, $_POST['userGroup'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user group
|
||||
*/
|
||||
if (! empty($_POST['editUserGroupSubmit'])) {
|
||||
UserGroups::edit($configurableMenusFeature, $_POST['userGroup']);
|
||||
}
|
||||
|
||||
if (isset($_POST['viewUsers'])) {
|
||||
// Display users belonging to a user group
|
||||
$this->response->addHTML(UserGroups::getHtmlForListingUsersofAGroup(
|
||||
$configurableMenusFeature,
|
||||
$_POST['userGroup']
|
||||
));
|
||||
}
|
||||
|
||||
if (isset($_GET['addUserGroup'])) {
|
||||
// Display add user group dialog
|
||||
$this->response->addHTML(UserGroups::getHtmlToEditUserGroup($configurableMenusFeature));
|
||||
} elseif (isset($_POST['editUserGroup'])) {
|
||||
// Display edit user group dialog
|
||||
$this->response->addHTML(UserGroups::getHtmlToEditUserGroup(
|
||||
$configurableMenusFeature,
|
||||
$_POST['userGroup']
|
||||
));
|
||||
} else {
|
||||
// Display user groups table
|
||||
$this->response->addHTML(UserGroups::getHtmlForUserGroupsTable($configurableMenusFeature));
|
||||
}
|
||||
|
||||
$this->response->addHTML('</div>');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\CheckUserPrivileges;
|
||||
use PhpMyAdmin\ConfigStorage\Features\ConfigurableMenusFeature;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
|
||||
final class UserGroupsFormController extends AbstractController
|
||||
{
|
||||
/** @var Relation */
|
||||
private $relation;
|
||||
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(
|
||||
ResponseRenderer $response,
|
||||
Template $template,
|
||||
Relation $relation,
|
||||
DatabaseInterface $dbi
|
||||
) {
|
||||
parent::__construct($response, $template);
|
||||
$this->relation = $relation;
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$this->response->setAjax(true);
|
||||
|
||||
if (! isset($_GET['username']) || strlen((string) $_GET['username']) === 0) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->setHttpResponseCode(400);
|
||||
$this->response->addJSON('message', __('Missing parameter:') . ' username');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$username = $_GET['username'];
|
||||
|
||||
$checkUserPrivileges = new CheckUserPrivileges($this->dbi);
|
||||
$checkUserPrivileges->getPrivileges();
|
||||
|
||||
$configurableMenusFeature = $this->relation->getRelationParameters()->configurableMenusFeature;
|
||||
if ($configurableMenusFeature === null) {
|
||||
$this->response->setRequestStatus(false);
|
||||
$this->response->setHttpResponseCode(400);
|
||||
$this->response->addJSON('message', __('User groups management is not enabled.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$form = $this->getHtmlToChooseUserGroup($username, $configurableMenusFeature);
|
||||
|
||||
$this->response->addJSON('message', $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a dropdown to select the user group with menu items configured to each of them.
|
||||
*/
|
||||
private function getHtmlToChooseUserGroup(
|
||||
string $username,
|
||||
ConfigurableMenusFeature $configurableMenusFeature
|
||||
): string {
|
||||
$groupTable = Util::backquote($configurableMenusFeature->database)
|
||||
. '.' . Util::backquote($configurableMenusFeature->userGroups);
|
||||
$userTable = Util::backquote($configurableMenusFeature->database)
|
||||
. '.' . Util::backquote($configurableMenusFeature->users);
|
||||
|
||||
$sqlQuery = sprintf(
|
||||
'SELECT `usergroup` FROM %s WHERE `username` = \'%s\'',
|
||||
$userTable,
|
||||
$this->dbi->escapeString($username)
|
||||
);
|
||||
$userGroup = $this->dbi->fetchValue($sqlQuery, 0, DatabaseInterface::CONNECT_CONTROL);
|
||||
|
||||
$allUserGroups = [];
|
||||
$sqlQuery = 'SELECT DISTINCT `usergroup` FROM ' . $groupTable;
|
||||
$result = $this->dbi->tryQueryAsControlUser($sqlQuery);
|
||||
if ($result) {
|
||||
while ($row = $result->fetchRow()) {
|
||||
$allUserGroups[$row[0]] = $row[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->template->render('server/privileges/choose_user_group', [
|
||||
'all_user_groups' => $allUserGroups,
|
||||
'user_group' => $userGroup,
|
||||
'params' => ['username' => $username],
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Variables;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function header;
|
||||
use function implode;
|
||||
|
||||
final class GetVariableController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params Request parameters
|
||||
*/
|
||||
public function __invoke(ServerRequest $request, array $params): void
|
||||
{
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send with correct charset
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
// Do not use double quotes inside the query to avoid a problem
|
||||
// when server is running in ANSI_QUOTES sql_mode
|
||||
$varValue = $this->dbi->fetchSingleRow(
|
||||
'SHOW GLOBAL VARIABLES WHERE Variable_name=\''
|
||||
. $this->dbi->escapeString($params['name']) . '\';',
|
||||
DatabaseInterface::FETCH_NUM
|
||||
);
|
||||
|
||||
$json = [
|
||||
'message' => $varValue[1],
|
||||
];
|
||||
|
||||
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($params['name']);
|
||||
|
||||
if ($variableType === 'byte') {
|
||||
/** @var string[] $bytes */
|
||||
$bytes = Util::formatByteDown($varValue[1], 3, 3);
|
||||
$json['message'] = implode(' ', $bytes);
|
||||
}
|
||||
|
||||
$this->response->addJSON($json);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server\Variables;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
use function htmlspecialchars;
|
||||
use function implode;
|
||||
use function is_numeric;
|
||||
use function mb_strtolower;
|
||||
use function preg_match;
|
||||
use function trim;
|
||||
|
||||
final class SetVariableController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the AJAX request for setting value for a single variable
|
||||
*
|
||||
* @param array $vars Request parameters
|
||||
*/
|
||||
public function __invoke(ServerRequest $request, array $vars): void
|
||||
{
|
||||
if (! $this->response->isAjax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$value = (string) $request->getParsedBodyParam('varValue');
|
||||
$variableName = (string) $vars['name'];
|
||||
$matches = [];
|
||||
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($variableName);
|
||||
|
||||
if (
|
||||
$variableType === 'byte' && preg_match(
|
||||
'/^\s*(\d+(\.\d+)?)\s*(mb|kb|mib|kib|gb|gib)\s*$/i',
|
||||
$value,
|
||||
$matches
|
||||
)
|
||||
) {
|
||||
$exp = [
|
||||
'kb' => 1,
|
||||
'kib' => 1,
|
||||
'mb' => 2,
|
||||
'mib' => 2,
|
||||
'gb' => 3,
|
||||
'gib' => 3,
|
||||
];
|
||||
$value = (float) $matches[1] * 1024 ** $exp[mb_strtolower($matches[3])];
|
||||
} else {
|
||||
$value = $this->dbi->escapeString($value);
|
||||
}
|
||||
|
||||
if (! is_numeric($value)) {
|
||||
$value = "'" . $value . "'";
|
||||
}
|
||||
|
||||
$json = [];
|
||||
if (! preg_match('/[^a-zA-Z0-9_]+/', $variableName)) {
|
||||
$this->dbi->query('SET GLOBAL ' . $variableName . ' = ' . $value);
|
||||
// Some values are rounded down etc.
|
||||
$varValue = $this->dbi->fetchSingleRow(
|
||||
'SHOW GLOBAL VARIABLES WHERE Variable_name="'
|
||||
. $this->dbi->escapeString($variableName)
|
||||
. '";',
|
||||
DatabaseInterface::FETCH_NUM
|
||||
);
|
||||
[$formattedValue, $isHtmlFormatted] = $this->formatVariable($variableName, $varValue[1]);
|
||||
|
||||
if ($isHtmlFormatted === false) {
|
||||
$json['variable'] = htmlspecialchars($formattedValue);
|
||||
} else {
|
||||
$json['variable'] = $formattedValue;
|
||||
}
|
||||
} else {
|
||||
$this->response->setRequestStatus(false);
|
||||
$json['error'] = __('Setting variable failed');
|
||||
}
|
||||
|
||||
$this->response->addJSON($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Variable
|
||||
*
|
||||
* @param string $name variable name
|
||||
* @param int|string $value variable value
|
||||
*
|
||||
* @return array formatted string and bool if string is HTML formatted
|
||||
*/
|
||||
private function formatVariable($name, $value): array
|
||||
{
|
||||
$isHtmlFormatted = false;
|
||||
$formattedValue = $value;
|
||||
|
||||
if (is_numeric($value)) {
|
||||
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($name);
|
||||
|
||||
if ($variableType === 'byte') {
|
||||
$isHtmlFormatted = true;
|
||||
/** @var string[] $bytes */
|
||||
$bytes = Util::formatByteDown($value, 3, 3);
|
||||
$formattedValue = trim(
|
||||
$this->template->render(
|
||||
'server/variables/format_variable',
|
||||
[
|
||||
'valueTitle' => Util::formatNumber($value, 0),
|
||||
'value' => implode(' ', $bytes),
|
||||
]
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$formattedValue = Util::formatNumber($value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$formattedValue,
|
||||
$isHtmlFormatted,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Controllers\Server;
|
||||
|
||||
use PhpMyAdmin\Controllers\AbstractController;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Providers\ServerVariables\ServerVariablesProvider;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Template;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_numeric;
|
||||
use function str_replace;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* Handles viewing and editing server variables
|
||||
*/
|
||||
class VariablesController extends AbstractController
|
||||
{
|
||||
/** @var DatabaseInterface */
|
||||
private $dbi;
|
||||
|
||||
public function __construct(ResponseRenderer $response, Template $template, DatabaseInterface $dbi)
|
||||
{
|
||||
parent::__construct($response, $template);
|
||||
$this->dbi = $dbi;
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
global $errorUrl;
|
||||
|
||||
$params = ['filter' => $_GET['filter'] ?? null];
|
||||
$errorUrl = Url::getFromRoute('/');
|
||||
|
||||
if ($this->dbi->isSuperUser()) {
|
||||
$this->dbi->selectDb('mysql');
|
||||
}
|
||||
|
||||
$filterValue = ! empty($params['filter']) ? $params['filter'] : '';
|
||||
|
||||
$this->addScriptFiles(['server/variables.js']);
|
||||
|
||||
$variables = [];
|
||||
$serverVarsResult = $this->dbi->tryQuery('SHOW SESSION VARIABLES;');
|
||||
if ($serverVarsResult !== false) {
|
||||
$serverVarsSession = $serverVarsResult->fetchAllKeyPair();
|
||||
|
||||
unset($serverVarsResult);
|
||||
|
||||
$serverVars = $this->dbi->fetchResult('SHOW GLOBAL VARIABLES;', 0, 1);
|
||||
|
||||
// list of static (i.e. non-editable) system variables
|
||||
$staticVariables = ServerVariablesProvider::getImplementation()->getStaticVariables();
|
||||
|
||||
foreach ($serverVars as $name => $value) {
|
||||
$hasSessionValue = isset($serverVarsSession[$name])
|
||||
&& $serverVarsSession[$name] !== $value;
|
||||
$docLink = Generator::linkToVarDocumentation(
|
||||
$name,
|
||||
$this->dbi->isMariaDB(),
|
||||
str_replace('_', ' ', $name)
|
||||
);
|
||||
|
||||
[$formattedValue, $isEscaped] = $this->formatVariable($name, $value);
|
||||
if ($hasSessionValue) {
|
||||
[$sessionFormattedValue] = $this->formatVariable($name, $serverVarsSession[$name]);
|
||||
}
|
||||
|
||||
$variables[] = [
|
||||
'name' => $name,
|
||||
'is_editable' => ! in_array(strtolower($name), $staticVariables),
|
||||
'doc_link' => $docLink,
|
||||
'value' => $formattedValue,
|
||||
'is_escaped' => $isEscaped,
|
||||
'has_session_value' => $hasSessionValue,
|
||||
'session_value' => $sessionFormattedValue ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->render('server/variables/index', [
|
||||
'variables' => $variables,
|
||||
'filter_value' => $filterValue,
|
||||
'is_superuser' => $this->dbi->isSuperUser(),
|
||||
'is_mariadb' => $this->dbi->isMariaDB(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Variable
|
||||
*
|
||||
* @param string $name variable name
|
||||
* @param int|string $value variable value
|
||||
*
|
||||
* @return array formatted string and bool if string is HTML formatted
|
||||
*/
|
||||
private function formatVariable($name, $value): array
|
||||
{
|
||||
$isHtmlFormatted = false;
|
||||
$formattedValue = $value;
|
||||
|
||||
if (is_numeric($value)) {
|
||||
$variableType = ServerVariablesProvider::getImplementation()->getVariableType($name);
|
||||
|
||||
if ($variableType === 'byte') {
|
||||
$isHtmlFormatted = true;
|
||||
/** @var string[] $bytes */
|
||||
$bytes = Util::formatByteDown($value, 3, 3);
|
||||
$formattedValue = trim(
|
||||
$this->template->render(
|
||||
'server/variables/format_variable',
|
||||
[
|
||||
'valueTitle' => Util::formatNumber($value, 0),
|
||||
'value' => implode(' ', $bytes),
|
||||
]
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$formattedValue = Util::formatNumber($value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$formattedValue,
|
||||
$isHtmlFormatted,
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue