Merge pull request #19149 from MauricioFauth/response-renderer-disable-removal

Remove ResponseRenderer::disable() from controllers
This commit is contained in:
Maurício Meneghini Fauth 2024-05-07 19:12:31 -03:00 committed by GitHub
commit 7ba6d987aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 611 additions and 360 deletions

View File

@ -57,7 +57,7 @@ return [
],
ChangeLogController::class => [
'class' => ChangeLogController::class,
'arguments' => ['$response' => '@response', '$config' => '@config'],
'arguments' => ['@response', '@config', '@' . ResponseFactory::class, '@template'],
],
CheckRelationsController::class => [
'class' => CheckRelationsController::class,
@ -219,7 +219,7 @@ return [
],
Database\Structure\AddPrefixController::class => [
'class' => Database\Structure\AddPrefixController::class,
'arguments' => ['$response' => '@response'],
'arguments' => ['@response', '@' . ResponseFactory::class, '@template'],
],
Database\Structure\AddPrefixTableController::class => [
'class' => Database\Structure\AddPrefixTableController::class,
@ -251,11 +251,11 @@ return [
],
Database\Structure\ChangePrefixFormController::class => [
'class' => Database\Structure\ChangePrefixFormController::class,
'arguments' => ['$response' => '@response'],
'arguments' => ['@response', '@' . ResponseFactory::class, '@template'],
],
Database\Structure\CopyFormController::class => [
'class' => Database\Structure\CopyFormController::class,
'arguments' => ['$response' => '@response'],
'arguments' => ['@response', '@' . ResponseFactory::class, '@template'],
],
Database\Structure\CopyTableController::class => [
'class' => Database\Structure\CopyTableController::class,
@ -448,12 +448,9 @@ return [
],
LicenseController::class => [
'class' => LicenseController::class,
'arguments' => ['$response' => '@response'],
],
LintController::class => [
'class' => LintController::class,
'arguments' => ['$response' => '@response'],
'arguments' => ['@response', '@' . ResponseFactory::class],
],
LintController::class => ['class' => LintController::class, 'arguments' => ['@' . ResponseFactory::class]],
LogoutController::class => [
'class' => LogoutController::class,
'arguments' => ['@' . AuthenticationPluginFactory::class],
@ -541,7 +538,7 @@ return [
],
PhpInfoController::class => [
'class' => PhpInfoController::class,
'arguments' => ['$response' => '@response'],
'arguments' => ['@response', '@' . ResponseFactory::class, '@config'],
],
Preferences\ExportController::class => [
'class' => Preferences\ExportController::class,
@ -591,6 +588,7 @@ return [
'$relation' => '@relation',
'$config' => '@config',
'$themeManager' => '@' . PhpMyAdmin\Theme\ThemeManager::class,
'$responseFactory' => '@' . ResponseFactory::class,
],
],
Preferences\NavigationController::class => [
@ -619,7 +617,7 @@ return [
],
SchemaExportController::class => [
'class' => SchemaExportController::class,
'arguments' => ['$export' => '@export', '$response' => '@response'],
'arguments' => ['@export', '@response', '@' . ResponseFactory::class],
],
Server\BinlogController::class => [
'class' => Server\BinlogController::class,
@ -988,7 +986,7 @@ return [
],
Table\GetFieldController::class => [
'class' => Table\GetFieldController::class,
'arguments' => ['$response' => '@response', '$dbi' => '@dbi'],
'arguments' => ['@response', '@dbi', '@' . ResponseFactory::class],
],
Table\GisVisualizationController::class => [
'class' => Table\GisVisualizationController::class,
@ -997,6 +995,7 @@ return [
'$template' => '@template',
'$dbi' => '@dbi',
'$dbTableExists' => '@' . DbTableExists::class,
'$responseFactory' => '@' . ResponseFactory::class,
],
],
Table\ImportController::class => [
@ -1263,6 +1262,7 @@ return [
'$tracking' => '@tracking',
'$trackingChecker' => '@tracking_checker',
'$dbTableExists' => '@' . DbTableExists::class,
'$responseFactory' => '@' . ResponseFactory::class,
],
],
Triggers\IndexController::class => [
@ -1318,6 +1318,7 @@ return [
'$relation' => '@relation',
'$dbi' => '@dbi',
'$dbTableExists' => '@' . DbTableExists::class,
'$responseFactory' => '@' . ResponseFactory::class,
],
],
UserPasswordController::class => [
@ -1326,7 +1327,7 @@ return [
],
VersionCheckController::class => [
'class' => VersionCheckController::class,
'arguments' => ['$response' => '@response', '$versionInformation' => '@version_information'],
'arguments' => ['@version_information', '@' . ResponseFactory::class],
],
View\CreateController::class => [
'class' => View\CreateController::class,

View File

@ -2786,7 +2786,7 @@ parameters:
path: src/Controllers/LicenseController.php
-
message: "#^Parameter \\#2 \\.\\.\\.\\$values of function printf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
message: "#^Parameter \\#1 \\$path of function basename expects string, mixed given\\.$#"
count: 1
path: src/Controllers/LicenseController.php
@ -4380,16 +4380,6 @@ parameters:
count: 3
path: src/Controllers/Table/GetFieldController.php
-
message: "#^Parameter \\#1 \\$string of function mb_strlen expects string, string\\|null given\\.$#"
count: 1
path: src/Controllers/Table/GetFieldController.php
-
message: "#^Parameter \\#1 \\$test of static method PhpMyAdmin\\\\Mime\\:\\:detect\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/Controllers/Table/GetFieldController.php
-
message: "#^Cannot access offset 'back' on mixed\\.$#"
count: 1

View File

@ -811,11 +811,6 @@
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/ChangeLogController.php">
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/CheckRelationsController.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
@ -1305,11 +1300,6 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/Database/Structure/AddPrefixController.php">
<PossiblyUnusedMethod>
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/Database/Structure/AddPrefixTableController.php">
<MixedOperand>
<code><![CDATA[$request->getParsedBodyParam('add_prefix', '')]]></code>
@ -1339,18 +1329,10 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/Database/Structure/ChangePrefixFormController.php">
<PossiblyUnusedMethod>
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/Database/Structure/CopyFormController.php">
<DeprecatedMethod>
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
</DeprecatedMethod>
<PossiblyUnusedMethod>
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/Database/Structure/CopyTableController.php">
<MixedArgument>
@ -2045,19 +2027,11 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/LicenseController.php">
<PossiblyUnusedMethod>
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/LintController.php">
<MixedAssignment>
<code><![CDATA[$editorType]]></code>
<code><![CDATA[$options]]></code>
</MixedAssignment>
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/LogoutController.php">
<PossiblyUnusedReturnValue>
@ -2327,14 +2301,6 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/PhpInfoController.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
</DeprecatedMethod>
<PossiblyUnusedMethod>
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/Preferences/ExportController.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
@ -2537,11 +2503,6 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/SchemaExportController.php">
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/Server/BinlogController.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
@ -3513,14 +3474,6 @@
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/Table/GetFieldController.php">
<PossiblyNullArgument>
<code><![CDATA[$result]]></code>
</PossiblyNullArgument>
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/Table/GisVisualizationController.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
@ -3537,9 +3490,6 @@
<PossiblyInvalidCast>
<code><![CDATA[$_GET['fileFormat']]]></code>
</PossiblyInvalidCast>
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
<RiskyCast>
<code><![CDATA[$_POST['pos'] ?? $_GET['pos'] ?? $_SESSION['tmpval']['pos']]]></code>
<code><![CDATA[$_POST['session_max_rows'] ?? $_GET['session_max_rows']]]></code>
@ -4206,9 +4156,6 @@
<code><![CDATA[$toggleActivation]]></code>
<code><![CDATA[$version]]></code>
</MixedAssignment>
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/Table/ZoomSearchController.php">
<DeprecatedMethod>
@ -4382,11 +4329,6 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/VersionCheckController.php">
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
</PossiblyUnusedReturnValue>
</file>
<file src="src/Controllers/View/CreateController.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
@ -12955,6 +12897,11 @@
<code><![CDATA[$config->settings]]></code>
</InvalidPropertyAssignmentValue>
</file>
<file src="tests/unit/Controllers/PhpInfoControllerTest.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>
</DeprecatedMethod>
</file>
<file src="tests/unit/Controllers/Server/BinlogControllerTest.php">
<DeprecatedMethod>
<code><![CDATA[Config::getInstance()]]></code>

View File

@ -3,3 +3,4 @@
window.location = '{{ url|escape('js') }}';
};
</script>
{{ t('Taking you to the target site.') }}

View File

@ -103,7 +103,7 @@ class Application
$requestHandler->add(new RequestProblemChecking($this->template, $this->responseFactory));
$requestHandler->add(new CurrentServerGlobalSetting($this->config));
$requestHandler->add(new ThemeInitialization());
$requestHandler->add(new UrlRedirection($this->config));
$requestHandler->add(new UrlRedirection($this->config, $this->template, $this->responseFactory));
$requestHandler->add(new SetupPageRedirection($this->config, $this->responseFactory));
$requestHandler->add(new MinimumCommonRedirection($this->config, $this->responseFactory));
$requestHandler->add(new LanguageAndThemeCookieSaving($this->config));

View File

@ -6,9 +6,11 @@ namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Config;
use PhpMyAdmin\Core;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Url;
use function __;
@ -20,20 +22,26 @@ use function is_readable;
use function ob_get_clean;
use function ob_start;
use function preg_replace;
use function printf;
use function readgzfile;
use function sprintf;
use function str_ends_with;
final class ChangeLogController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response, private readonly Config $config)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly Config $config,
private readonly ResponseFactory $responseFactory,
private readonly Template $template,
) {
}
public function __invoke(ServerRequest $request): Response|null
public function __invoke(ServerRequest $request): Response
{
$this->response->disable();
$this->response->getHeader()->sendHttpHeaders();
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
$filename = $this->config->getChangeLogFilePath();
@ -42,16 +50,12 @@ final class ChangeLogController implements InvocableController
*/
// Check if the file is available, some distributions remove these.
if (! @is_readable($filename)) {
printf(
__(
'The %s file is not available on this system, please visit %s for more information.',
),
return $response->write(sprintf(
__('The %s file is not available on this system, please visit %s for more information.'),
basename($filename),
'<a href="' . Core::linkURL('https://www.phpmyadmin.net/')
. '" rel="noopener noreferrer" target="_blank">phpmyadmin.net</a>',
);
return null;
));
}
// Test if the file is in a compressed format
@ -109,12 +113,8 @@ final class ChangeLogController implements InvocableController
'/a href="/' => 'a target="_blank" rel="noopener noreferrer" href="',
];
$this->response->addHeader('Content-Type', 'text/html; charset=utf-8');
$this->response->render('changelog', [
return $response->write($this->template->render('changelog', [
'changelog' => preg_replace(array_keys($replaces), $replaces, $changelog),
]);
return null;
]));
}
}

View File

@ -6,16 +6,21 @@ namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\InvocableController;
use PhpMyAdmin\Current;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class AddPrefixController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly ResponseFactory $responseFactory,
private readonly Template $template,
) {
}
public function __invoke(ServerRequest $request): Response|null
@ -35,9 +40,11 @@ final class AddPrefixController implements InvocableController
$params['selected'][] = $selectedValue;
}
$this->response->disable();
$this->response->render('database/structure/add_prefix', ['url_params' => $params]);
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
return null;
return $response->write($this->template->render('database/structure/add_prefix', ['url_params' => $params]));
}
}

View File

@ -6,16 +6,21 @@ namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\InvocableController;
use PhpMyAdmin\Current;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class ChangePrefixFormController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly ResponseFactory $responseFactory,
private readonly Template $template,
) {
}
public function __invoke(ServerRequest $request): Response|null
@ -40,12 +45,14 @@ final class ChangePrefixFormController implements InvocableController
$urlParams['selected'][] = $selectedValue;
}
$this->response->disable();
$this->response->render('database/structure/change_prefix_form', [
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
return $response->write($this->template->render('database/structure/change_prefix_form', [
'route' => $route,
'url_params' => $urlParams,
]);
return null;
]));
}
}

View File

@ -7,16 +7,21 @@ namespace PhpMyAdmin\Controllers\Database\Structure;
use PhpMyAdmin\Controllers\InvocableController;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use function __;
final class CopyFormController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly ResponseFactory $responseFactory,
private readonly Template $template,
) {
}
public function __invoke(ServerRequest $request): Response|null
@ -44,12 +49,14 @@ final class CopyFormController implements InvocableController
}
}
$this->response->disable();
$this->response->render('database/structure/copy_form', [
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
return $response->write($this->template->render('database/structure/copy_form', [
'url_params' => $urlParams,
'options' => $databasesList->getList(),
]);
return null;
]));
}
}

View File

@ -1,52 +1,59 @@
<?php
/**
* Simple script to set correct charset for the license
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use function __;
use function basename;
use function is_readable;
use function printf;
use function ob_get_clean;
use function ob_start;
use function readfile;
use function sprintf;
/**
* Simple script to set correct charset for the license
*/
final class LicenseController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly ResponseFactory $responseFactory,
) {
}
public function __invoke(ServerRequest $request): Response|null
public function __invoke(ServerRequest $request): Response
{
$this->response->disable();
$this->response->addHeader('Content-Type', 'text/plain; charset=utf-8');
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
$filename = LICENSE_FILE;
// Check if the file is available, some distributions remove these.
if (@is_readable($filename)) {
readfile($filename);
return null;
if (! @is_readable($filename)) {
return $response->write(sprintf(
__('The %s file is not available on this system, please visit %s for more information.'),
basename($filename),
'<a href="' . Core::linkURL('https://www.phpmyadmin.net/')
. '" rel="noopener noreferrer" target="_blank">phpmyadmin.net</a>',
));
}
printf(
__(
'The %s file is not available on this system, please visit %s for more information.',
),
$filename,
'https://www.phpmyadmin.net/',
);
$response = $response->withHeader('Content-Type', 'text/plain; charset=utf-8');
return null;
ob_start();
readfile($filename);
$license = (string) ob_get_clean();
return $response->write($license);
}
}

View File

@ -1,23 +1,18 @@
<?php
/**
* Represents the interface between the linter and the query editor.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Linter;
use PhpMyAdmin\ResponseRenderer;
use function header;
use function is_array;
use function is_string;
use function json_encode;
use function sprintf;
/**
* Represents the interface between the linter and the query editor.
@ -30,7 +25,7 @@ final class LintController implements InvocableController
'trigger' => "DELIMITER $$ CREATE TRIGGER `a` AFTER INSERT ON `b` FOR EACH ROW\n",
];
public function __construct(private readonly ResponseRenderer $response)
public function __construct(private readonly ResponseFactory $responseFactory)
{
}
@ -70,15 +65,11 @@ final class LintController implements InvocableController
}
}
// Disabling standard response.
$this->response->disable();
$response = $this->responseFactory->createResponse();
foreach (Core::headerJSON() as $name => $value) {
header(sprintf('%s: %s', $name, $value));
$response = $response->withHeader($name, $value);
}
echo json_encode($lints);
return null;
return $response->write((string) json_encode($lints));
}
}

View File

@ -1,17 +1,18 @@
<?php
/**
* phpinfo() wrapper to allow displaying only when configured to do so.
*/
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Config;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use function ob_get_clean;
use function ob_start;
use function phpinfo;
use const INFO_CONFIGURATION;
@ -23,21 +24,30 @@ use const INFO_MODULES;
*/
final class PhpInfoController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly ResponseFactory $responseFactory,
private readonly Config $config,
) {
}
public function __invoke(ServerRequest $request): Response|null
public function __invoke(ServerRequest $request): Response
{
$this->response->disable();
$this->response->getHeader()->sendHttpHeaders();
if (! Config::getInstance()->settings['ShowPhpInfo']) {
return null;
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES);
if (! $this->config->settings['ShowPhpInfo']) {
$response = $response->withHeader('Location', $this->response->fixRelativeUrlForRedirect('./'));
return null;
return $response->withStatus(StatusCodeInterface::STATUS_FOUND);
}
ob_start();
phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES);
$phpInfo = (string) ob_get_clean();
return $response->write($phpInfo);
}
}

View File

@ -11,6 +11,7 @@ use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\InvocableController;
use PhpMyAdmin\Core;
use PhpMyAdmin\File;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
@ -51,6 +52,7 @@ final class ManageController implements InvocableController
private readonly Relation $relation,
private readonly Config $config,
private readonly ThemeManager $themeManager,
private readonly ResponseFactory $responseFactory,
) {
}
@ -69,29 +71,29 @@ final class ManageController implements InvocableController
$GLOBALS['error'] = '';
if ($request->hasBodyParam('submit_export') && $request->getParsedBodyParam('export_type') === 'text_file') {
// export to JSON file
$this->response->disable();
$response = $this->responseFactory->createResponse();
$filename = 'phpMyAdmin-config-' . urlencode(Core::getEnv('HTTP_HOST')) . '.json';
Core::downloadHeader($filename, 'application/json');
$settings = $this->userPreferences->load();
echo json_encode($settings['config_data'], JSON_PRETTY_PRINT);
return null;
return $response->write((string) json_encode($settings['config_data'], JSON_PRETTY_PRINT));
}
if ($request->hasBodyParam('submit_export') && $request->getParsedBodyParam('export_type') === 'php_file') {
// export to JSON file
$this->response->disable();
// export to PHP file
$response = $this->responseFactory->createResponse();
$filename = 'phpMyAdmin-config-' . urlencode(Core::getEnv('HTTP_HOST')) . '.php';
Core::downloadHeader($filename, 'application/php');
$settings = $this->userPreferences->load();
echo '/* ' . __('phpMyAdmin configuration snippet') . " */\n\n";
echo '/* ' . __('Paste it to your config.inc.php') . " */\n\n";
$output = '/* ' . __('phpMyAdmin configuration snippet') . " */\n\n";
$output .= '/* ' . __('Paste it to your config.inc.php') . " */\n\n";
foreach ($settings['config_data'] as $key => $val) {
echo '$cfg[\'' . str_replace('/', '\'][\'', $key) . '\'] = ';
echo var_export($val, true) . ";\n";
$output .= '$cfg[\'' . str_replace('/', '\'][\'', $key) . '\'] = ';
$output .= var_export($val, true) . ";\n";
}
return null;
return $response->write($output);
}
if ($request->hasBodyParam('submit_get_json')) {

View File

@ -8,6 +8,7 @@ use PhpMyAdmin\Core;
use PhpMyAdmin\Exceptions\ExportException;
use PhpMyAdmin\Export\Export;
use PhpMyAdmin\Html\MySQLDocumentation;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
@ -23,8 +24,11 @@ use function mb_strlen;
*/
final class SchemaExportController implements InvocableController
{
public function __construct(private readonly Export $export, private readonly ResponseRenderer $response)
{
public function __construct(
private readonly Export $export,
private readonly ResponseRenderer $response,
private readonly ResponseFactory $responseFactory,
) {
}
public function __invoke(ServerRequest $request): Response|null
@ -54,14 +58,13 @@ final class SchemaExportController implements InvocableController
return null;
}
$this->response->disable();
$response = $this->responseFactory->createResponse();
Core::downloadHeader(
$exportInfo['fileName'],
$exportInfo['mediaType'],
mb_strlen($exportInfo['fileData'], '8bit'),
);
echo $exportInfo['fileData'];
return null;
return $response->write($exportInfo['fileData']);
}
}

View File

@ -9,6 +9,7 @@ use PhpMyAdmin\Core;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Message;
@ -27,14 +28,15 @@ use function sprintf;
*/
final class GetFieldController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response, private readonly DatabaseInterface $dbi)
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly DatabaseInterface $dbi,
private readonly ResponseFactory $responseFactory,
) {
}
public function __invoke(ServerRequest $request): Response|null
{
$this->response->disable();
if (! $this->response->checkParameters(['db', 'table'])) {
return null;
}
@ -83,16 +85,18 @@ final class GetFieldController implements InvocableController
return null;
}
$result ??= '';
/* Avoid corrupting data */
ini_set('url_rewriter.tags', '');
$response = $this->responseFactory->createResponse();
Core::downloadHeader(
Current::$table . '-' . $transformKey . '.bin',
Mime::detect($result),
mb_strlen($result, '8bit'),
);
echo $result;
return null;
return $response->write($result);
}
}

View File

@ -13,6 +13,7 @@ use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\FieldMetadata;
use PhpMyAdmin\Gis\GisVisualization;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
@ -26,6 +27,8 @@ use function __;
use function in_array;
use function is_array;
use function is_string;
use function ob_get_clean;
use function ob_start;
/**
* Handles creation of the GIS visualizations.
@ -37,6 +40,7 @@ final class GisVisualizationController implements InvocableController
private readonly Template $template,
private readonly DatabaseInterface $dbi,
private readonly DbTableExists $dbTableExists,
private readonly ResponseFactory $responseFactory,
) {
}
@ -112,11 +116,13 @@ final class GisVisualizationController implements InvocableController
$visualization = GisVisualization::get($sqlQuery, $visualizationSettings, $rows, $pos);
if (isset($_GET['saveToFile'])) {
$this->response->disable();
$response = $this->responseFactory->createResponse();
$filename = $visualization->getSpatialColumn();
ob_start();
$visualization->toFile($filename, $_GET['fileFormat']);
$output = ob_get_clean();
return null;
return $response->write((string) $output);
}
$this->response->addScriptFiles(['vendor/openlayers/OpenLayers.js', 'table/gis_visualization.js']);

View File

@ -10,6 +10,7 @@ use PhpMyAdmin\Controllers\InvocableController;
use PhpMyAdmin\Core;
use PhpMyAdmin\Current;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
@ -42,6 +43,7 @@ final class TrackingController implements InvocableController
private readonly Tracking $tracking,
private readonly TrackingChecker $trackingChecker,
private readonly DbTableExists $dbTableExists,
private readonly ResponseFactory $responseFactory,
) {
}
@ -138,11 +140,10 @@ final class TrackingController implements InvocableController
// Export as file download
if ($reportExportType === 'sqldumpfile') {
$downloadInfo = $this->tracking->getDownloadInfoForExport($tableParam, $entries);
$this->response->disable();
$response = $this->responseFactory->createResponse();
Core::downloadHeader($downloadInfo['filename'], 'text/x-sql', mb_strlen($downloadInfo['dump']));
echo $downloadInfo['dump'];
return null;
return $response->write($downloadInfo['dump']);
}
// Export as SQL execution

View File

@ -9,6 +9,7 @@ use PhpMyAdmin\Controllers\InvocableController;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
@ -24,6 +25,8 @@ use function __;
use function htmlspecialchars;
use function is_numeric;
use function is_string;
use function ob_get_clean;
use function ob_start;
use function round;
use function sprintf;
use function str_contains;
@ -42,6 +45,7 @@ final class WrapperController implements InvocableController
private readonly Relation $relation,
private readonly DatabaseInterface $dbi,
private readonly DbTableExists $dbTableExists,
private readonly ResponseFactory $responseFactory,
) {
}
@ -107,9 +111,10 @@ final class WrapperController implements InvocableController
}
}
// Disabling standard response, we are sending binary here
$this->response->disable();
$this->response->getHeader()->sendHttpHeaders();
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
/** @psalm-suppress MixedAssignment */
$contentType = $request->getParam('ct');
@ -133,19 +138,15 @@ final class WrapperController implements InvocableController
$resize = $request->getParam('resize');
if ($resize !== 'jpeg' && $resize !== 'png') {
if (str_contains(strtolower($contentMediaType), 'html')) {
echo htmlspecialchars($row[$transformKey]);
return null;
return $response->write(htmlspecialchars($row[$transformKey]));
}
echo $row[$transformKey];
return null;
return $response->write($row[$transformKey]);
}
$srcImage = ImageWrapper::fromString($row[$transformKey]);
if ($srcImage === null) {
return null;
return $response;
}
$newHeight = $this->formatSize($request->getParam('newHeight'));
@ -172,18 +173,21 @@ final class WrapperController implements InvocableController
$destImage = ImageWrapper::create($destWidth, $destHeight);
if ($destImage === null) {
return null;
return $response;
}
$destImage->copyResampled($srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
ob_start();
if ($resize === 'jpeg') {
$destImage->jpeg(null, 75);
} else {
$destImage->png();
}
return null;
$output = ob_get_clean();
return $response->write((string) $output);
}
private function formatSize(mixed $size): int

View File

@ -5,14 +5,12 @@ declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Core;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\VersionInformation;
use function header;
use function json_encode;
use function sprintf;
/**
* A caching proxy for retrieving version information from https://www.phpmyadmin.net/.
@ -20,29 +18,22 @@ use function sprintf;
final class VersionCheckController implements InvocableController
{
public function __construct(
private readonly ResponseRenderer $response,
private readonly VersionInformation $versionInformation,
private readonly ResponseFactory $responseFactory,
) {
}
public function __invoke(ServerRequest $request): Response|null
public function __invoke(ServerRequest $request): Response
{
$_GET['ajax_request'] = 'true';
// Disabling standard response.
$this->response->disable();
// Always send the correct headers
$response = $this->responseFactory->createResponse();
foreach (Core::headerJSON() as $name => $value) {
header(sprintf('%s: %s', $name, $value));
$response = $response->withHeader($name, $value);
}
$versionDetails = $this->versionInformation->getLatestVersions();
if ($versionDetails === null) {
echo json_encode([]);
return null;
return $response->write((string) json_encode([]));
}
$latestCompatible = $this->versionInformation->getLatestCompatibleVersion($versionDetails);
@ -53,8 +44,6 @@ final class VersionCheckController implements InvocableController
$date = $latestCompatible->date;
}
echo json_encode(['version' => $version, 'date' => $date]);
return null;
return $response->write((string) json_encode(['version' => $version, 'date' => $date]));
}
}

View File

@ -6,6 +6,9 @@ namespace PhpMyAdmin\Http\Middleware;
use PhpMyAdmin\Config;
use PhpMyAdmin\Container\ContainerBuilder;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Theme\ThemeManager;
use PhpMyAdmin\UrlRedirector;
use Psr\Http\Message\ResponseInterface;
@ -13,12 +16,13 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function is_string;
final class UrlRedirection implements MiddlewareInterface
{
public function __construct(private readonly Config $config)
{
public function __construct(
private readonly Config $config,
private readonly Template $template,
private readonly ResponseFactory $responseFactory,
) {
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
@ -32,11 +36,8 @@ final class UrlRedirection implements MiddlewareInterface
$themeManager = $container->get(ThemeManager::class);
$this->config->loadUserPreferences($themeManager, true);
return UrlRedirector::redirect($this->getUrlParam($request->getQueryParams()['url'] ?? null));
}
$urlRedirector = new UrlRedirector(ResponseRenderer::getInstance(), $this->template, $this->responseFactory);
private function getUrlParam(mixed $url): string
{
return is_string($url) ? $url : '';
return $urlRedirector->redirect($request->getQueryParams()['url'] ?? null);
}
}

View File

@ -428,21 +428,26 @@ class ResponseRenderer
throw new ExitException($message);
}
/**
* Avoid relative path redirect problems in case user entered URL
* like /phpmyadmin/index.php/ which some web servers happily accept.
*/
public function fixRelativeUrlForRedirect(string $url): string
{
if (! str_starts_with($url, '.')) {
return $url;
}
return $this->config->getRootPath() . substr($url, 2);
}
/**
* @psalm-param non-empty-string $url
* @psalm-param StatusCodeInterface::STATUS_* $statusCode
*/
public function redirect(string $url, int $statusCode = StatusCodeInterface::STATUS_FOUND): void
{
/**
* Avoid relative path redirect problems in case user entered URL
* like /phpmyadmin/index.php/ which some web servers happily accept.
*/
if (str_starts_with($url, '.')) {
$url = $this->config->getRootPath() . substr($url, 2);
}
$this->addHeader('Location', $url);
$this->addHeader('Location', $this->fixRelativeUrlForRedirect($url));
$this->setStatusCode($statusCode);
}

View File

@ -4,10 +4,11 @@ declare(strict_types=1);
namespace PhpMyAdmin;
use PhpMyAdmin\Container\ContainerBuilder;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Response;
use function __;
use function is_string;
use function preg_match;
/**
@ -15,37 +16,36 @@ use function preg_match;
*/
final class UrlRedirector
{
public static function redirect(string $url): Response
public function __construct(
private readonly ResponseRenderer $response,
private readonly Template $template,
private readonly ResponseFactory $responseFactory,
) {
}
public function redirect(mixed $urlParam): Response
{
$container = ContainerBuilder::getContainer();
// Only output the http headers
$response = ResponseRenderer::getInstance();
$response->getHeader()->sendHttpHeaders();
$response->disable();
$response = $this->responseFactory->createResponse();
foreach ($this->response->getHeader()->getHttpHeaders() as $name => $value) {
$response = $response->withHeader($name, $value);
}
$url = is_string($urlParam) ? $urlParam : '';
if (
$url === ''
|| ! preg_match('/^https:\/\/[^\n\r]*$/', $url)
|| ! Core::isAllowedDomain($url)
) {
$response->redirect('./');
$response = $response->withHeader('Location', $this->response->fixRelativeUrlForRedirect('./'));
return $response->response();
return $response->withStatus(StatusCodeInterface::STATUS_FOUND);
}
/**
* JavaScript redirection is necessary. Because if header() is used then web browser sometimes does not change
* the HTTP_REFERER field and so with old URL as Referer, token also goes to external site.
*
* @var Template $template
*/
$template = $container->get('template');
echo $template->render('javascript/redirect', ['url' => $url]);
// Display redirecting msg on screen.
// Do not display the value of $_GET['url'] to avoid showing injected content
echo __('Taking you to the target site.');
return $response->response();
return $response->write($this->template->render('javascript/redirect', ['url' => $url]));
}
}

View File

@ -4,8 +4,10 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Config;
use PhpMyAdmin\Controllers\ChangeLogController;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
@ -27,34 +29,33 @@ final class ChangeLogControllerTest extends AbstractTestCase
$responseRenderer = new ResponseRenderer();
$template = new Template();
$controller = new ChangeLogController($responseRenderer, $config);
$controller($request);
$controller = new ChangeLogController($responseRenderer, $config, ResponseFactory::create(), $template);
$response = $controller($request);
self::assertTrue($responseRenderer->isDisabled());
$response = $responseRenderer->getResponse();
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
// phpcs:disable Generic.Files.LineLength.TooLong
$changelog = <<<'HTML'
phpMyAdmin - ChangeLog
======================
phpMyAdmin - ChangeLog
======================
5.2.2 (not yet released)
- <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://github.com/phpmyadmin/phpmyadmin/issues/17522">issue #17522</a> Fix case where the routes cache file is invalid
- issue Upgrade slim/psr7 to 1.4.1 for <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://www.cve.org/CVERecord?id=CVE-2023-30536">CVE-2023-30536</a> - GHSA-q2qj-628g-vhfw
5.2.2 (not yet released)
- <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://github.com/phpmyadmin/phpmyadmin/issues/17522">issue #17522</a> Fix case where the routes cache file is invalid
- issue Upgrade slim/psr7 to 1.4.1 for <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://www.cve.org/CVERecord?id=CVE-2023-30536">CVE-2023-30536</a> - GHSA-q2qj-628g-vhfw
5.2.1 (2023-02-07)
- <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://github.com/phpmyadmin/phpmyadmin/issues/16418">issue #16418</a> Fix <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://docs.phpmyadmin.net/en/latest/faq.html#faq1-44">FAQ 1.44</a> about manually removing vendor folders
- issue [security] Fix an XSS attack through the drag-and-drop upload feature (<a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://www.phpmyadmin.net/security/PMASA-2023-01/">PMASA-2023-01</a>)
5.2.1 (2023-02-07)
- <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://github.com/phpmyadmin/phpmyadmin/issues/16418">issue #16418</a> Fix <a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://docs.phpmyadmin.net/en/latest/faq.html#faq1-44">FAQ 1.44</a> about manually removing vendor folders
- issue [security] Fix an XSS attack through the drag-and-drop upload feature (<a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://www.phpmyadmin.net/security/PMASA-2023-01/">PMASA-2023-01</a>)
--- Older ChangeLogs can be found on our project website ---
<a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://www.phpmyadmin.net/old-stuff/ChangeLogs/">https://www.phpmyadmin.net/old-stuff/ChangeLogs/</a>
--- Older ChangeLogs can be found on our project website ---
<a target="_blank" rel="noopener noreferrer" href="index.php?route=/url&lang=en&url=https://www.phpmyadmin.net/old-stuff/ChangeLogs/">https://www.phpmyadmin.net/old-stuff/ChangeLogs/</a>
HTML;
HTML;
// phpcs:enable
$expected = $template->render('changelog', ['changelog' => $changelog]);
self::assertSame($expected, $responseRenderer->getHTMLResult());
self::assertSame($expected, (string) $response->getBody());
}
#[RequiresPhpExtension('zlib')]
@ -66,16 +67,18 @@ HTML;
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/');
$responseRenderer = new ResponseRenderer();
$controller = new ChangeLogController($responseRenderer, $config);
$controller($request);
$controller = new ChangeLogController($responseRenderer, $config, ResponseFactory::create(), new Template());
$response = $controller($request);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
self::assertStringContainsString(
'- <a target="_blank" rel="noopener noreferrer"'
. ' href="index.php?route=/url&lang=en&url=https://github.com/phpmyadmin/phpmyadmin/issues/16418">'
. 'issue #16418</a> Fix <a target="_blank" rel="noopener noreferrer"'
. ' href="index.php?route=/url&lang=en&url=https://docs.phpmyadmin.net/en/latest/faq.html#faq1-44">'
. 'FAQ 1.44</a> about manually removing vendor folders',
$responseRenderer->getHTMLResult(),
(string) $response->getBody(),
);
}
@ -87,15 +90,16 @@ HTML;
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/');
$responseRenderer = new ResponseRenderer();
$controller = new ChangeLogController($responseRenderer, $config);
$controller($request);
$controller = new ChangeLogController($responseRenderer, $config, ResponseFactory::create(), new Template());
$response = $controller($request);
self::assertSame('', $responseRenderer->getHTMLResult());
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
self::assertSame(
'The InvalidChangeLog file is not available on this system, please visit'
. ' <a href="index.php?route=/url&url=https%3A%2F%2Fwww.phpmyadmin.net%2F"'
. ' rel="noopener noreferrer" target="_blank">phpmyadmin.net</a> for more information.',
self::getActualOutputForAssertion(),
(string) $response->getBody(),
);
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers\Database\Structure;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\Database\Structure\AddPrefixController;
use PhpMyAdmin\Current;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
#[CoversClass(AddPrefixController::class)]
final class AddPrefixControllerTest extends AbstractTestCase
{
public function testAddPrefixModal(): void
{
Current::$database = 'test_db';
$request = ServerRequestFactory::create()->createServerRequest('POST', 'http://example.com/')
->withQueryParams(['db' => 'test_db'])
->withParsedBody(['selected_tbl' => ['test_table']]);
$template = new Template();
$controller = new AddPrefixController(new ResponseRenderer(), ResponseFactory::create(), $template);
$response = $controller($request);
$expected = $template->render(
'database/structure/add_prefix',
['url_params' => ['db' => 'test_db', 'selected' => ['test_table']]],
);
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
self::assertSame($expected, (string) $response->getBody());
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers\Database\Structure;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\Database\Structure\ChangePrefixFormController;
use PhpMyAdmin\Current;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
#[CoversClass(ChangePrefixFormController::class)]
final class ChangePrefixFormControllerTest extends AbstractTestCase
{
public function testChangePrefixModal(): void
{
Current::$database = 'test_db';
$request = ServerRequestFactory::create()->createServerRequest('POST', 'http://example.com/')
->withQueryParams(['db' => 'test_db'])
->withParsedBody(['selected_tbl' => ['test_table']]);
$template = new Template();
$controller = new ChangePrefixFormController(new ResponseRenderer(), ResponseFactory::create(), $template);
$response = $controller($request);
$expected = $template->render('database/structure/change_prefix_form', [
'route' => '/database/structure/replace-prefix',
'url_params' => ['db' => 'test_db', 'selected' => ['test_table']],
]);
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
self::assertSame($expected, (string) $response->getBody());
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers\Database\Structure;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\Database\Structure\CopyFormController;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
#[CoversClass(CopyFormController::class)]
final class CopyFormControllerTest extends AbstractTestCase
{
public function testCopyFormModal(): void
{
$dbiDummy = $this->createDbiDummy();
$dbiDummy->addResult(
'SELECT `SCHEMA_NAME` FROM `INFORMATION_SCHEMA`.`SCHEMATA`',
[['test_db'], ['test_db_1'], ['test_db_2']],
['SCHEMA_NAME'],
);
DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy);
Current::$database = 'test_db';
$request = ServerRequestFactory::create()->createServerRequest('POST', 'http://example.com/')
->withQueryParams(['db' => 'test_db'])
->withParsedBody(['selected_tbl' => ['test_table']]);
$template = new Template();
$controller = new CopyFormController(new ResponseRenderer(), ResponseFactory::create(), $template);
$response = $controller($request);
$expected = $template->render('database/structure/copy_form', [
'url_params' => ['db' => 'test_db', 'selected' => ['test_table']],
'options' => [
['name' => 'test_db_1', 'is_selected' => false],
['name' => 'test_db_2', 'is_selected' => false],
],
]);
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
self::assertSame($expected, (string) $response->getBody());
$dbiDummy->assertAllQueriesConsumed();
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\LicenseController;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
#[CoversClass(LicenseController::class)]
final class LicenseControllerTest extends AbstractTestCase
{
public function testWithValidFile(): void
{
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/');
$controller = new LicenseController(new ResponseRenderer(), ResponseFactory::create());
$response = $controller($request);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/plain; charset=utf-8'], $response->getHeader('Content-Type'));
$body = (string) $response->getBody();
self::assertStringContainsString('GNU GENERAL PUBLIC LICENSE', $body);
self::assertStringContainsString('Version 2, June 1991', $body);
}
}

View File

@ -4,19 +4,18 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\LintController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use function json_encode;
#[CoversClass(LintController::class)]
#[RunTestsInSeparateProcesses]
class LintControllerTest extends AbstractTestCase
final class LintControllerTest extends AbstractTestCase
{
protected function setUp(): void
{
@ -31,9 +30,12 @@ class LintControllerTest extends AbstractTestCase
$request->method('isAjax')->willReturn(true);
$request->method('getParsedBodyParam')->willReturnMap([['sql_query', '', ''], ['options', null, null]]);
$this->getLintController()($request);
$response = $this->getLintController()($request);
$output = $this->getActualOutputForAssertion();
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['application/json; charset=UTF-8'], $response->getHeader('Content-Type'));
$output = (string) $response->getBody();
self::assertJson($output);
self::assertJsonStringEqualsJsonString('[]', $output);
}
@ -47,9 +49,12 @@ class LintControllerTest extends AbstractTestCase
['options', null, null],
]);
$this->getLintController()($request);
$response = $this->getLintController()($request);
$output = $this->getActualOutputForAssertion();
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['application/json; charset=UTF-8'], $response->getHeader('Content-Type'));
$output = (string) $response->getBody();
self::assertJson($output);
self::assertJsonStringEqualsJsonString('[]', $output);
}
@ -99,15 +104,18 @@ class LintControllerTest extends AbstractTestCase
['options', null, null],
]);
$this->getLintController()($request);
$response = $this->getLintController()($request);
$output = $this->getActualOutputForAssertion();
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['application/json; charset=UTF-8'], $response->getHeader('Content-Type'));
$output = (string) $response->getBody();
self::assertJson($output);
self::assertJsonStringEqualsJsonString($expectedJson, $output);
}
private function getLintController(): LintController
{
return new LintController(new ResponseRenderer());
return new LintController(ResponseFactory::create());
}
}

View File

@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Config;
use PhpMyAdmin\Controllers\PhpInfoController;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
use function ob_get_clean;
use function ob_start;
use function phpinfo;
use const INFO_CONFIGURATION;
use const INFO_GENERAL;
use const INFO_MODULES;
#[CoversClass(PhpInfoController::class)]
final class PhpInfoControllerTest extends AbstractTestCase
{
public function testWithShowPhpInfoEqualsTrue(): void
{
$config = new Config();
$config->settings['ShowPhpInfo'] = true;
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/');
$controller = new PhpInfoController(new ResponseRenderer(), ResponseFactory::create(), $config);
$response = $controller($request);
ob_start();
phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES);
$expected = (string) ob_get_clean();
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['text/html; charset=utf-8'], $response->getHeader('Content-Type'));
self::assertSame($expected, (string) $response->getBody());
}
public function testWithShowPhpInfoEqualsFalse(): void
{
$config = Config::getInstance();
$config->settings['ShowPhpInfo'] = false;
$config->settings['PmaAbsoluteUri'] = 'http://localhost/phpmyadmin';
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/');
$controller = new PhpInfoController(new ResponseRenderer(), ResponseFactory::create(), $config);
$response = $controller($request);
self::assertSame(StatusCodeInterface::STATUS_FOUND, $response->getStatusCode());
self::assertSame('/phpmyadmin/', $response->getHeaderLine('Location'));
}
}

View File

@ -4,16 +4,23 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\SchemaExportController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Export\Export;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use function function_exists;
use function xdebug_get_headers;
#[CoversClass(SchemaExportController::class)]
class SchemaExportControllerTest extends AbstractTestCase
#[RunTestsInSeparateProcesses]
final class SchemaExportControllerTest extends AbstractTestCase
{
public function testExport(): void
{
@ -28,13 +35,20 @@ class SchemaExportControllerTest extends AbstractTestCase
'fileData' => 'file data',
]);
$response = new ResponseRenderer();
$controller = new SchemaExportController($export, $response);
$controller($request);
$output = $this->getActualOutputForAssertion();
self::assertSame('file data', $output);
self::assertTrue($response->isDisabled());
self::assertSame('', $response->getHTMLResult());
self::assertSame([], $response->getJSONResult());
$controller = new SchemaExportController($export, new ResponseRenderer(), ResponseFactory::create());
$response = $controller($request);
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame('file data', (string) $response->getBody());
if (! function_exists('xdebug_get_headers')) {
return;
}
$headersList = xdebug_get_headers();
self::assertContains('Content-Disposition: attachment; filename="file.svg"', $headersList);
self::assertContains('Content-Type: image/svg+xml', $headersList);
self::assertContains('Content-Length: 9', $headersList);
}
}

View File

@ -4,10 +4,12 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers\Table;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\Table\GetFieldController;
use PhpMyAdmin\Core;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
@ -51,7 +53,10 @@ class GetFieldControllerTest extends AbstractTestCase
$dbi = $this->createDatabaseInterface($dummyDbi);
DatabaseInterface::$instance = $dbi;
(new GetFieldController(new ResponseRenderer(), $dbi))($request);
$this->expectOutputString('46494c45');
$response = (new GetFieldController(new ResponseRenderer(), $dbi, ResponseFactory::create()))($request);
self::assertNotNull($response);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame('46494c45', (string) $response->getBody());
}
}

View File

@ -9,6 +9,7 @@ use PhpMyAdmin\Core;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
@ -111,8 +112,17 @@ class GisVisualizationControllerTest extends AbstractTestCase
$request = ServerRequestFactory::create()->createServerRequest('POST', 'http://example.com/')
->withQueryParams(['db' => 'test_db', 'table' => 'test_table']);
$response = new ResponseRenderer();
(new GisVisualizationController($response, $template, $dbi, new DbTableExists($dbi)))($request);
self::assertSame($expected, $response->getHTMLResult());
$responseRenderer = new ResponseRenderer();
$controller = new GisVisualizationController(
$responseRenderer,
$template,
$dbi,
new DbTableExists($dbi),
ResponseFactory::create(),
);
$response = $controller($request);
self::assertNull($response);
self::assertSame($expected, $responseRenderer->getHTMLResult());
}
}

View File

@ -11,6 +11,7 @@ use PhpMyAdmin\Controllers\Table\TrackingController;
use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\SqlQueryForm;
use PhpMyAdmin\Template;
@ -51,13 +52,13 @@ class TrackingControllerTest extends AbstractTestCase
$request = ServerRequestFactory::create()->createServerRequest('GET', 'http://example.com/')
->withQueryParams(['db' => 'test_db', 'table' => 'test_table']);
$response = new ResponseRenderer();
$responseRenderer = new ResponseRenderer();
$template = new Template();
$trackingChecker = self::createStub(TrackingChecker::class);
$relation = new Relation($this->dbi);
$bookmarkRepository = new BookmarkRepository($this->dbi, $relation);
(new TrackingController(
$response,
$response = (new TrackingController(
$responseRenderer,
new Tracking(
new SqlQueryForm($template, $this->dbi, $bookmarkRepository),
$template,
@ -67,8 +68,11 @@ class TrackingControllerTest extends AbstractTestCase
),
$trackingChecker,
new DbTableExists($this->dbi),
ResponseFactory::create(),
))($request);
self::assertNull($response);
$main = $template->render('table/tracking/main', [
'url_params' => [
'db' => Current::$database,
@ -101,6 +105,6 @@ class TrackingControllerTest extends AbstractTestCase
'main' => $main,
]);
self::assertSame($expected, $response->getHTMLResult());
self::assertSame($expected, $responseRenderer->getHTMLResult());
}
}

View File

@ -4,19 +4,18 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests\Controllers;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Controllers\VersionCheckController;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Release;
use PhpMyAdmin\Tests\AbstractTestCase;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PhpMyAdmin\VersionInformation;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
#[CoversClass(VersionCheckController::class)]
#[RunTestsInSeparateProcesses]
class VersionCheckControllerTest extends AbstractTestCase
final class VersionCheckControllerTest extends AbstractTestCase
{
protected function setUp(): void
{
@ -27,7 +26,6 @@ class VersionCheckControllerTest extends AbstractTestCase
public function testWithLatestCompatibleVersion(): void
{
$_GET = [];
$versionInfo = [
new Release('5.1.3', '2022-02-11', '>=7.1,<8.1', '>=5.5'),
new Release('4.9.10', '2022-02-11', '>=5.5,<8.0', '>=5.5'),
@ -39,19 +37,18 @@ class VersionCheckControllerTest extends AbstractTestCase
->with(self::equalTo($versionInfo))
->willReturn($versionInfo[0]);
(new VersionCheckController(
new ResponseRenderer(),
$response = (new VersionCheckController(
$versionInformation,
ResponseFactory::create(),
))(self::createStub(ServerRequest::class));
$output = $this->getActualOutputForAssertion();
self::assertTrue(isset($_GET['ajax_request']));
self::assertSame('{"version":"5.1.3","date":"2022-02-11"}', $output);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['application/json; charset=UTF-8'], $response->getHeader('Content-Type'));
self::assertSame('{"version":"5.1.3","date":"2022-02-11"}', (string) $response->getBody());
}
public function testWithoutLatestCompatibleVersion(): void
{
$_GET = [];
$versionInfo = [
new Release('5.1.3', '2022-02-11', '>=7.1,<8.1', '>=5.5'),
new Release('4.9.10', '2022-02-11', '>=5.5,<8.0', '>=5.5'),
@ -63,31 +60,29 @@ class VersionCheckControllerTest extends AbstractTestCase
->with(self::equalTo($versionInfo))
->willReturn(null);
(new VersionCheckController(
new ResponseRenderer(),
$response = (new VersionCheckController(
$versionInformation,
ResponseFactory::create(),
))(self::createStub(ServerRequest::class));
$output = $this->getActualOutputForAssertion();
self::assertTrue(isset($_GET['ajax_request']));
self::assertSame('{"version":"","date":""}', $output);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['application/json; charset=UTF-8'], $response->getHeader('Content-Type'));
self::assertSame('{"version":"","date":""}', (string) $response->getBody());
}
public function testWithoutLatestVersion(): void
{
$_GET = [];
$versionInformation = $this->createMock(VersionInformation::class);
$versionInformation->expects(self::once())->method('getLatestVersions')->willReturn(null);
$versionInformation->expects(self::never())->method('getLatestCompatibleVersion');
(new VersionCheckController(
new ResponseRenderer(),
$response = (new VersionCheckController(
$versionInformation,
ResponseFactory::create(),
))(self::createStub(ServerRequest::class));
$output = $this->getActualOutputForAssertion();
self::assertTrue(isset($_GET['ajax_request']));
self::assertSame('[]', $output);
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
self::assertSame(['application/json; charset=UTF-8'], $response->getHeader('Content-Type'));
self::assertSame('[]', (string) $response->getBody());
}
}

View File

@ -172,11 +172,6 @@ class ResponseRenderer extends \PhpMyAdmin\ResponseRenderer
return $this->isAjax;
}
public function isDisabled(): bool
{
return $this->isDisabled;
}
public function getResponse(): Response
{
return $this->response;

View File

@ -6,43 +6,44 @@ namespace PhpMyAdmin\Tests;
use Fig\Http\Message\StatusCodeInterface;
use PhpMyAdmin\Config;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\Stubs\ResponseRenderer;
use PhpMyAdmin\UrlRedirector;
use PHPUnit\Framework\Attributes\CoversClass;
use ReflectionProperty;
#[CoversClass(UrlRedirector::class)]
final class UrlRedirectorTest extends AbstractTestCase
{
public function testRedirectWithDisallowedUrl(): void
{
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
$GLOBALS['lang'] = 'en';
$config = Config::getInstance();
$config->settings['PmaAbsoluteUri'] = 'http://localhost/phpmyadmin';
$response = UrlRedirector::redirect('https://user:pass@example.com/');
$urlRedirector = new UrlRedirector(new ResponseRenderer(), new Template(), ResponseFactory::create());
$response = $urlRedirector->redirect('https://user:pass@example.com/');
self::assertSame('/phpmyadmin/', $response->getHeaderLine('Location'));
self::assertSame(StatusCodeInterface::STATUS_FOUND, $response->getStatusCode());
}
public function testRedirectWithAllowedUrl(): void
{
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
$GLOBALS['lang'] = 'en';
$_SERVER['SERVER_NAME'] = 'localhost';
UrlRedirector::redirect('https://phpmyadmin.net/');
$output = self::getActualOutputForAssertion();
$expected = <<<'HTML'
<script>
window.onload = function () {
window.location = 'https\u003A\/\/phpmyadmin.net\/';
};
</script>
Taking you to the target site.
HTML;
$urlRedirector = new UrlRedirector(new ResponseRenderer(), new Template(), ResponseFactory::create());
self::assertSame($expected, $output);
$response = $urlRedirector->redirect('https://phpmyadmin.net/');
$expected = <<<'HTML'
<script>
window.onload = function () {
window.location = 'https\u003A\/\/phpmyadmin.net\/';
};
</script>
Taking you to the target site.
HTML;
self::assertSame($expected, (string) $response->getBody());
}
}