Skip to content

Commit 898fb91

Browse files
Merge pull request #19151 from MauricioFauth/stmt-history-middleware
Extract Footer::setHistory() into a middleware
2 parents 2a58575 + 5e429ff commit 898fb91

6 files changed

Lines changed: 174 additions & 70 deletions

File tree

phpstan-baseline.neon

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7602,7 +7602,7 @@ parameters:
76027602

76037603
-
76047604
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
7605-
count: 4
7605+
count: 2
76067606
path: src/Footer.php
76077607

76087608
-
@@ -7620,11 +7620,6 @@ parameters:
76207620
count: 1
76217621
path: src/Footer.php
76227622

7623-
-
7624-
message: "#^Parameter \\#4 \\$sqlquery of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:setHistory\\(\\) expects string, mixed given\\.$#"
7625-
count: 1
7626-
path: src/Footer.php
7627-
76287623
-
76297624
message: "#^Only booleans are allowed in an elseif condition, int\\|false given\\.$#"
76307625
count: 1
@@ -8230,6 +8225,16 @@ parameters:
82308225
count: 1
82318226
path: src/Http/Middleware/SetupPageRedirection.php
82328227

8228+
-
8229+
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
8230+
count: 1
8231+
path: src/Http/Middleware/StatementHistory.php
8232+
8233+
-
8234+
message: "#^Parameter \\#4 \\$sqlquery of method PhpMyAdmin\\\\ConfigStorage\\\\Relation\\:\\:setHistory\\(\\) expects string, mixed given\\.$#"
8235+
count: 1
8236+
path: src/Http/Middleware/StatementHistory.php
8237+
82338238
-
82348239
message: "#^Parameter \\#1 \\$known_string of function hash_equals expects string, mixed given\\.$#"
82358240
count: 1

psalm-baseline.xml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5996,10 +5996,6 @@
59965996
</MixedOperand>
59975997
</file>
59985998
<file src="src/Footer.php">
5999-
<DeprecatedMethod>
6000-
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
6001-
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
6002-
</DeprecatedMethod>
60035999
<DeprecatedProperty>
60046000
<code><![CDATA[Routing::$route]]></code>
60056001
</DeprecatedProperty>
@@ -6014,13 +6010,8 @@
60146010
<code><![CDATA[array{revision: string, revisionUrl: string, branch: string, branchUrl: string}|[]]]></code>
60156011
<code><![CDATA[is_array($info) ? $info : []]]></code>
60166012
</MixedReturnTypeCoercion>
6017-
<RedundantCast>
6018-
<code><![CDATA[(string) $_REQUEST['no_history']]]></code>
6019-
</RedundantCast>
60206013
<RiskyTruthyFalsyComparison>
60216014
<code><![CDATA[$object]]></code>
6022-
<code><![CDATA[empty($GLOBALS['error_message'])]]></code>
6023-
<code><![CDATA[empty($GLOBALS['sql_query'])]]></code>
60246015
<code><![CDATA[empty($_REQUEST['no_debug'])]]></code>
60256016
</RiskyTruthyFalsyComparison>
60266017
<UnusedReturnValue>
@@ -6522,6 +6513,17 @@
65226513
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
65236514
</DeprecatedMethod>
65246515
</file>
6516+
<file src="src/Http/Middleware/StatementHistory.php">
6517+
<DeprecatedMethod>
6518+
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
6519+
</DeprecatedMethod>
6520+
<MixedArgument>
6521+
<code><![CDATA[$GLOBALS['sql_query']]]></code>
6522+
</MixedArgument>
6523+
<RiskyTruthyFalsyComparison>
6524+
<code><![CDATA[empty($GLOBALS['error_message'])]]></code>
6525+
</RiskyTruthyFalsyComparison>
6526+
</file>
65256527
<file src="src/Http/Middleware/TokenRequestParamChecking.php">
65266528
<MixedArgument>
65276529
<code><![CDATA[$_SESSION[' PMA_token ']]]></code>

src/Application.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
use PhpMyAdmin\Http\Middleware\SetupPageRedirection;
3939
use PhpMyAdmin\Http\Middleware\SqlDelimiterSetting;
4040
use PhpMyAdmin\Http\Middleware\SqlQueryGlobalSetting;
41+
use PhpMyAdmin\Http\Middleware\StatementHistory;
4142
use PhpMyAdmin\Http\Middleware\ThemeInitialization;
4243
use PhpMyAdmin\Http\Middleware\TokenMismatchChecking;
4344
use PhpMyAdmin\Http\Middleware\TokenRequestParamChecking;
@@ -116,6 +117,7 @@ public function run(bool $isSetupPage = false): void
116117
$requestHandler->add(new ProfilingChecking());
117118
$requestHandler->add(new UserPreferencesLoading($this->config));
118119
$requestHandler->add(new RecentTableHandling($this->config));
120+
$requestHandler->add(new StatementHistory($this->config));
119121

120122
$runner = new RequestHandlerRunner(
121123
$requestHandler,

src/Footer.php

Lines changed: 23 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
namespace PhpMyAdmin;
99

10-
use PhpMyAdmin\ConfigStorage\Relation;
1110
use PhpMyAdmin\Error\ErrorHandler;
1211
use PhpMyAdmin\Routing\Routing;
1312
use Traversable;
@@ -17,7 +16,6 @@
1716
use function in_array;
1817
use function is_array;
1918
use function is_object;
20-
use function is_scalar;
2119
use function json_encode;
2220
use function json_last_error;
2321

@@ -44,12 +42,9 @@ class Footer
4442
*/
4543
private bool $isEnabled = true;
4644

47-
private Relation $relation;
48-
4945
public function __construct(private readonly Template $template, private readonly Config $config)
5046
{
5147
$this->scripts = new Scripts($this->template);
52-
$this->relation = new Relation(DatabaseInterface::getInstance());
5348
}
5449

5550
/**
@@ -160,32 +155,6 @@ public function getErrorMessages(): string
160155
return $retval;
161156
}
162157

163-
/**
164-
* Saves query in history
165-
*/
166-
private function setHistory(): void
167-
{
168-
if (
169-
(
170-
isset($_REQUEST['no_history'])
171-
&& is_scalar($_REQUEST['no_history'])
172-
&& (string) $_REQUEST['no_history'] !== ''
173-
)
174-
|| ! empty($GLOBALS['error_message'])
175-
|| empty($GLOBALS['sql_query'])
176-
|| ! DatabaseInterface::getInstance()->isConnected()
177-
) {
178-
return;
179-
}
180-
181-
$this->relation->setHistory(
182-
Current::$database,
183-
Current::$table,
184-
$this->config->selectedServer['user'],
185-
$GLOBALS['sql_query'],
186-
);
187-
}
188-
189158
/**
190159
* Disables the rendering of the footer
191160
*/
@@ -228,36 +197,35 @@ public function getScripts(): Scripts
228197
*/
229198
public function getDisplay(): string
230199
{
231-
$this->setHistory();
232-
if ($this->isEnabled) {
233-
if (! $this->isAjax && ! $this->isMinimal) {
234-
if (Core::getEnv('SCRIPT_NAME') !== '') {
235-
$url = $this->getSelfUrl();
236-
}
200+
if (! $this->isEnabled) {
201+
return '';
202+
}
237203

238-
$this->scripts->addCode('window.Console.debugSqlInfo = ' . $this->getDebugMessage() . ';');
239-
$errorMessages = $this->getErrorMessages();
240-
$scripts = $this->scripts->getDisplay();
204+
if (! $this->isAjax && ! $this->isMinimal) {
205+
if (Core::getEnv('SCRIPT_NAME') !== '') {
206+
$url = $this->getSelfUrl();
207+
}
241208

242-
if ($this->config->config->debug->demo) {
243-
$gitRevisionInfo = $this->getGitRevisionInfo();
244-
}
209+
$this->scripts->addCode('window.Console.debugSqlInfo = ' . $this->getDebugMessage() . ';');
210+
$errorMessages = $this->getErrorMessages();
211+
$scripts = $this->scripts->getDisplay();
245212

246-
$footer = Config::renderFooter();
213+
if ($this->config->config->debug->demo) {
214+
$gitRevisionInfo = $this->getGitRevisionInfo();
247215
}
248216

249-
return $this->template->render('footer', [
250-
'is_ajax' => $this->isAjax,
251-
'is_minimal' => $this->isMinimal,
252-
'self_url' => $url ?? null,
253-
'error_messages' => $errorMessages ?? '',
254-
'scripts' => $scripts ?? '',
255-
'is_demo' => $this->config->config->debug->demo,
256-
'git_revision_info' => $gitRevisionInfo ?? [],
257-
'footer' => $footer ?? '',
258-
]);
217+
$footer = Config::renderFooter();
259218
}
260219

261-
return '';
220+
return $this->template->render('footer', [
221+
'is_ajax' => $this->isAjax,
222+
'is_minimal' => $this->isMinimal,
223+
'self_url' => $url ?? null,
224+
'error_messages' => $errorMessages ?? '',
225+
'scripts' => $scripts ?? '',
226+
'is_demo' => $this->config->config->debug->demo,
227+
'git_revision_info' => $gitRevisionInfo ?? [],
228+
'footer' => $footer ?? '',
229+
]);
262230
}
263231
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpMyAdmin\Http\Middleware;
6+
7+
use PhpMyAdmin\Config;
8+
use PhpMyAdmin\ConfigStorage\Relation;
9+
use PhpMyAdmin\Current;
10+
use PhpMyAdmin\DatabaseInterface;
11+
use PhpMyAdmin\Http\ServerRequest;
12+
use Psr\Http\Message\ResponseInterface;
13+
use Psr\Http\Message\ServerRequestInterface;
14+
use Psr\Http\Server\MiddlewareInterface;
15+
use Psr\Http\Server\RequestHandlerInterface;
16+
17+
use function assert;
18+
19+
final class StatementHistory implements MiddlewareInterface
20+
{
21+
private readonly Relation $relation;
22+
private readonly DatabaseInterface $dbi;
23+
24+
public function __construct(
25+
private readonly Config $config,
26+
DatabaseInterface|null $dbi = null,
27+
Relation|null $relation = null,
28+
) {
29+
$this->dbi = $dbi ?? DatabaseInterface::getInstance();
30+
$this->relation = $relation ?? new Relation($this->dbi, $this->config);
31+
}
32+
33+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
34+
{
35+
assert($request instanceof ServerRequest);
36+
$response = $handler->handle($request);
37+
38+
if (
39+
! $request->has('no_history')
40+
&& empty($GLOBALS['error_message'])
41+
&& $GLOBALS['sql_query'] !== ''
42+
&& $this->dbi->isConnected()
43+
) {
44+
$this->relation->setHistory(
45+
Current::$database,
46+
Current::$table,
47+
$this->config->selectedServer['user'],
48+
$GLOBALS['sql_query'],
49+
);
50+
}
51+
52+
return $response;
53+
}
54+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpMyAdmin\Tests\Http\Middleware;
6+
7+
use PhpMyAdmin\Config;
8+
use PhpMyAdmin\ConfigStorage\Relation;
9+
use PhpMyAdmin\Current;
10+
use PhpMyAdmin\DatabaseInterface;
11+
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
12+
use PhpMyAdmin\Http\Middleware\StatementHistory;
13+
use PhpMyAdmin\Tests\AbstractTestCase;
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\Attributes\TestWith;
16+
use Psr\Http\Message\ResponseInterface;
17+
use Psr\Http\Server\RequestHandlerInterface;
18+
19+
#[CoversClass(StatementHistory::class)]
20+
final class StatementHistoryTest extends AbstractTestCase
21+
{
22+
public function testStatementHistory(): void
23+
{
24+
Current::$database = 'test_db';
25+
Current::$table = 'test_table';
26+
$GLOBALS['sql_query'] = 'SELECT 1;';
27+
28+
$config = new Config();
29+
$config->selectedServer['user'] = 'test_user';
30+
$dbi = self::createStub(DatabaseInterface::class);
31+
$dbi->method('isConnected')->willReturn(true);
32+
$relation = self::createMock(Relation::class);
33+
$relation->expects(self::once())->method('setHistory')->with(
34+
self::identicalTo('test_db'),
35+
self::identicalTo('test_table'),
36+
self::identicalTo('test_user'),
37+
self::identicalTo('SELECT 1;'),
38+
);
39+
$statementHistory = new StatementHistory($config, $dbi, $relation);
40+
41+
$response = self::createStub(ResponseInterface::class);
42+
$handler = self::createStub(RequestHandlerInterface::class);
43+
$handler->method('handle')->willReturn($response);
44+
45+
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/');
46+
47+
self::assertSame($response, $statementHistory->process($request, $handler));
48+
}
49+
50+
#[TestWith(['true', 'SELECT 1;', true])]
51+
#[TestWith([null, '', true])]
52+
#[TestWith([null, 'SELECT 1;', false])]
53+
public function testSkipHistory(string|null $noHistoryParam, string $sqlQuery, bool $isConnected): void
54+
{
55+
$GLOBALS['sql_query'] = $sqlQuery;
56+
57+
$dbi = self::createStub(DatabaseInterface::class);
58+
$dbi->method('isConnected')->willReturn($isConnected);
59+
$relation = self::createMock(Relation::class);
60+
$relation->expects(self::never())->method('setHistory');
61+
$statementHistory = new StatementHistory(new Config(), $dbi, $relation);
62+
63+
$response = self::createStub(ResponseInterface::class);
64+
$handler = self::createStub(RequestHandlerInterface::class);
65+
$handler->method('handle')->willReturn($response);
66+
67+
$parsedBody = $noHistoryParam !== null ? ['no_history' => $noHistoryParam] : [];
68+
$request = ServerRequestFactory::create()->createServerRequest('POST', 'https://example.com/')
69+
->withParsedBody($parsedBody);
70+
71+
self::assertSame($response, $statementHistory->process($request, $handler));
72+
}
73+
}

0 commit comments

Comments
 (0)