diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index f78492c0ac..a16ea8d6ed 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -7602,7 +7602,7 @@ parameters:
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
- count: 4
+ count: 2
path: src/Footer.php
-
@@ -7620,11 +7620,6 @@ parameters:
count: 1
path: src/Footer.php
- -
- message: "#^Parameter \\#4 \\$sqlquery of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:setHistory\\(\\) expects string, mixed given\\.$#"
- count: 1
- path: src/Footer.php
-
-
message: "#^Only booleans are allowed in an elseif condition, int\\|false given\\.$#"
count: 1
@@ -8230,6 +8225,16 @@ parameters:
count: 1
path: src/Http/Middleware/SetupPageRedirection.php
+ -
+ message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
+ count: 1
+ path: src/Http/Middleware/StatementHistory.php
+
+ -
+ message: "#^Parameter \\#4 \\$sqlquery of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:setHistory\\(\\) expects string, mixed given\\.$#"
+ count: 1
+ path: src/Http/Middleware/StatementHistory.php
+
-
message: "#^Parameter \\#1 \\$known_string of function hash_equals expects string, mixed given\\.$#"
count: 1
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 4b925e6f88..a64a2a7cbb 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -5996,10 +5996,6 @@
-
-
-
-
@@ -6014,13 +6010,8 @@
-
-
-
-
-
@@ -6522,6 +6513,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Application.php b/src/Application.php
index 720563d637..736cc1a158 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -38,6 +38,7 @@ use PhpMyAdmin\Http\Middleware\SessionHandling;
use PhpMyAdmin\Http\Middleware\SetupPageRedirection;
use PhpMyAdmin\Http\Middleware\SqlDelimiterSetting;
use PhpMyAdmin\Http\Middleware\SqlQueryGlobalSetting;
+use PhpMyAdmin\Http\Middleware\StatementHistory;
use PhpMyAdmin\Http\Middleware\ThemeInitialization;
use PhpMyAdmin\Http\Middleware\TokenMismatchChecking;
use PhpMyAdmin\Http\Middleware\TokenRequestParamChecking;
@@ -116,6 +117,7 @@ class Application
$requestHandler->add(new ProfilingChecking());
$requestHandler->add(new UserPreferencesLoading($this->config));
$requestHandler->add(new RecentTableHandling($this->config));
+ $requestHandler->add(new StatementHistory($this->config));
$runner = new RequestHandlerRunner(
$requestHandler,
diff --git a/src/Footer.php b/src/Footer.php
index 46f3a240a4..8044d81a04 100644
--- a/src/Footer.php
+++ b/src/Footer.php
@@ -7,7 +7,6 @@ declare(strict_types=1);
namespace PhpMyAdmin;
-use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Error\ErrorHandler;
use PhpMyAdmin\Routing\Routing;
use Traversable;
@@ -17,7 +16,6 @@ use function file_exists;
use function in_array;
use function is_array;
use function is_object;
-use function is_scalar;
use function json_encode;
use function json_last_error;
@@ -44,12 +42,9 @@ class Footer
*/
private bool $isEnabled = true;
- private Relation $relation;
-
public function __construct(private readonly Template $template, private readonly Config $config)
{
$this->scripts = new Scripts($this->template);
- $this->relation = new Relation(DatabaseInterface::getInstance());
}
/**
@@ -160,32 +155,6 @@ class Footer
return $retval;
}
- /**
- * Saves query in history
- */
- private function setHistory(): void
- {
- if (
- (
- isset($_REQUEST['no_history'])
- && is_scalar($_REQUEST['no_history'])
- && (string) $_REQUEST['no_history'] !== ''
- )
- || ! empty($GLOBALS['error_message'])
- || empty($GLOBALS['sql_query'])
- || ! DatabaseInterface::getInstance()->isConnected()
- ) {
- return;
- }
-
- $this->relation->setHistory(
- Current::$database,
- Current::$table,
- $this->config->selectedServer['user'],
- $GLOBALS['sql_query'],
- );
- }
-
/**
* Disables the rendering of the footer
*/
@@ -228,36 +197,35 @@ class Footer
*/
public function getDisplay(): string
{
- $this->setHistory();
- if ($this->isEnabled) {
- if (! $this->isAjax && ! $this->isMinimal) {
- if (Core::getEnv('SCRIPT_NAME') !== '') {
- $url = $this->getSelfUrl();
- }
-
- $this->scripts->addCode('window.Console.debugSqlInfo = ' . $this->getDebugMessage() . ';');
- $errorMessages = $this->getErrorMessages();
- $scripts = $this->scripts->getDisplay();
-
- if ($this->config->config->debug->demo) {
- $gitRevisionInfo = $this->getGitRevisionInfo();
- }
-
- $footer = Config::renderFooter();
- }
-
- return $this->template->render('footer', [
- 'is_ajax' => $this->isAjax,
- 'is_minimal' => $this->isMinimal,
- 'self_url' => $url ?? null,
- 'error_messages' => $errorMessages ?? '',
- 'scripts' => $scripts ?? '',
- 'is_demo' => $this->config->config->debug->demo,
- 'git_revision_info' => $gitRevisionInfo ?? [],
- 'footer' => $footer ?? '',
- ]);
+ if (! $this->isEnabled) {
+ return '';
}
- return '';
+ if (! $this->isAjax && ! $this->isMinimal) {
+ if (Core::getEnv('SCRIPT_NAME') !== '') {
+ $url = $this->getSelfUrl();
+ }
+
+ $this->scripts->addCode('window.Console.debugSqlInfo = ' . $this->getDebugMessage() . ';');
+ $errorMessages = $this->getErrorMessages();
+ $scripts = $this->scripts->getDisplay();
+
+ if ($this->config->config->debug->demo) {
+ $gitRevisionInfo = $this->getGitRevisionInfo();
+ }
+
+ $footer = Config::renderFooter();
+ }
+
+ return $this->template->render('footer', [
+ 'is_ajax' => $this->isAjax,
+ 'is_minimal' => $this->isMinimal,
+ 'self_url' => $url ?? null,
+ 'error_messages' => $errorMessages ?? '',
+ 'scripts' => $scripts ?? '',
+ 'is_demo' => $this->config->config->debug->demo,
+ 'git_revision_info' => $gitRevisionInfo ?? [],
+ 'footer' => $footer ?? '',
+ ]);
}
}
diff --git a/src/Http/Middleware/StatementHistory.php b/src/Http/Middleware/StatementHistory.php
new file mode 100644
index 0000000000..d6aab96e71
--- /dev/null
+++ b/src/Http/Middleware/StatementHistory.php
@@ -0,0 +1,54 @@
+dbi = $dbi ?? DatabaseInterface::getInstance();
+ $this->relation = $relation ?? new Relation($this->dbi, $this->config);
+ }
+
+ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+ {
+ assert($request instanceof ServerRequest);
+ $response = $handler->handle($request);
+
+ if (
+ ! $request->has('no_history')
+ && empty($GLOBALS['error_message'])
+ && $GLOBALS['sql_query'] !== ''
+ && $this->dbi->isConnected()
+ ) {
+ $this->relation->setHistory(
+ Current::$database,
+ Current::$table,
+ $this->config->selectedServer['user'],
+ $GLOBALS['sql_query'],
+ );
+ }
+
+ return $response;
+ }
+}
diff --git a/tests/unit/Http/Middleware/StatementHistoryTest.php b/tests/unit/Http/Middleware/StatementHistoryTest.php
new file mode 100644
index 0000000000..079e049414
--- /dev/null
+++ b/tests/unit/Http/Middleware/StatementHistoryTest.php
@@ -0,0 +1,73 @@
+selectedServer['user'] = 'test_user';
+ $dbi = self::createStub(DatabaseInterface::class);
+ $dbi->method('isConnected')->willReturn(true);
+ $relation = self::createMock(Relation::class);
+ $relation->expects(self::once())->method('setHistory')->with(
+ self::identicalTo('test_db'),
+ self::identicalTo('test_table'),
+ self::identicalTo('test_user'),
+ self::identicalTo('SELECT 1;'),
+ );
+ $statementHistory = new StatementHistory($config, $dbi, $relation);
+
+ $response = self::createStub(ResponseInterface::class);
+ $handler = self::createStub(RequestHandlerInterface::class);
+ $handler->method('handle')->willReturn($response);
+
+ $request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/');
+
+ self::assertSame($response, $statementHistory->process($request, $handler));
+ }
+
+ #[TestWith(['true', 'SELECT 1;', true])]
+ #[TestWith([null, '', true])]
+ #[TestWith([null, 'SELECT 1;', false])]
+ public function testSkipHistory(string|null $noHistoryParam, string $sqlQuery, bool $isConnected): void
+ {
+ $GLOBALS['sql_query'] = $sqlQuery;
+
+ $dbi = self::createStub(DatabaseInterface::class);
+ $dbi->method('isConnected')->willReturn($isConnected);
+ $relation = self::createMock(Relation::class);
+ $relation->expects(self::never())->method('setHistory');
+ $statementHistory = new StatementHistory(new Config(), $dbi, $relation);
+
+ $response = self::createStub(ResponseInterface::class);
+ $handler = self::createStub(RequestHandlerInterface::class);
+ $handler->method('handle')->willReturn($response);
+
+ $parsedBody = $noHistoryParam !== null ? ['no_history' => $noHistoryParam] : [];
+ $request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
+ ->withParsedBody($parsedBody);
+
+ self::assertSame($response, $statementHistory->process($request, $handler));
+ }
+}