diff --git a/app/services_controllers.php b/app/services_controllers.php
index d87d3bd2ac..09b3d2859e 100644
--- a/app/services_controllers.php
+++ b/app/services_controllers.php
@@ -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,
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 4f8ffff0a0..f78492c0ac 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -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
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index e284b1f35c..0ca1317422 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -811,11 +811,6 @@
-
-
-
-
-
@@ -1305,11 +1300,6 @@
-
-
-
-
-
getParsedBodyParam('add_prefix', '')]]>
@@ -1339,18 +1329,10 @@
-
-
-
-
-
-
-
-
@@ -2045,19 +2027,11 @@
-
-
-
-
-
-
-
-
@@ -2327,14 +2301,6 @@
-
-
-
-
-
-
-
-
@@ -2537,11 +2503,6 @@
-
-
-
-
-
@@ -3513,14 +3474,6 @@
-
-
-
-
-
-
-
-
@@ -3537,9 +3490,6 @@
-
-
-
@@ -4206,9 +4156,6 @@
-
-
-
@@ -4382,11 +4329,6 @@
-
-
-
-
-
@@ -12955,6 +12897,11 @@
settings]]>
+
+
+
+
+
diff --git a/resources/templates/javascript/redirect.twig b/resources/templates/javascript/redirect.twig
index 7aebb65d06..7572cde105 100644
--- a/resources/templates/javascript/redirect.twig
+++ b/resources/templates/javascript/redirect.twig
@@ -3,3 +3,4 @@
window.location = '{{ url|escape('js') }}';
};
+{{ t('Taking you to the target site.') }}
diff --git a/src/Application.php b/src/Application.php
index d76589b6b3..720563d637 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -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));
diff --git a/src/Controllers/ChangeLogController.php b/src/Controllers/ChangeLogController.php
index a90c8afa9c..64f3bc0775 100644
--- a/src/Controllers/ChangeLogController.php
+++ b/src/Controllers/ChangeLogController.php
@@ -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),
'phpmyadmin.net',
- );
-
- 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;
+ ]));
}
}
diff --git a/src/Controllers/Database/Structure/AddPrefixController.php b/src/Controllers/Database/Structure/AddPrefixController.php
index d4a232c38e..8f4dae0b9e 100644
--- a/src/Controllers/Database/Structure/AddPrefixController.php
+++ b/src/Controllers/Database/Structure/AddPrefixController.php
@@ -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]));
}
}
diff --git a/src/Controllers/Database/Structure/ChangePrefixFormController.php b/src/Controllers/Database/Structure/ChangePrefixFormController.php
index c034db49fd..0672c5c7c4 100644
--- a/src/Controllers/Database/Structure/ChangePrefixFormController.php
+++ b/src/Controllers/Database/Structure/ChangePrefixFormController.php
@@ -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;
+ ]));
}
}
diff --git a/src/Controllers/Database/Structure/CopyFormController.php b/src/Controllers/Database/Structure/CopyFormController.php
index 00b4b5ae20..f3faf6f7f9 100644
--- a/src/Controllers/Database/Structure/CopyFormController.php
+++ b/src/Controllers/Database/Structure/CopyFormController.php
@@ -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;
+ ]));
}
}
diff --git a/src/Controllers/LicenseController.php b/src/Controllers/LicenseController.php
index 962a8929bb..b7dda4d2b4 100644
--- a/src/Controllers/LicenseController.php
+++ b/src/Controllers/LicenseController.php
@@ -1,52 +1,59 @@
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),
+ 'phpmyadmin.net',
+ ));
}
- 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);
}
}
diff --git a/src/Controllers/LintController.php b/src/Controllers/LintController.php
index d632034e1f..5db9fe6d20 100644
--- a/src/Controllers/LintController.php
+++ b/src/Controllers/LintController.php
@@ -1,23 +1,18 @@
"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));
}
}
diff --git a/src/Controllers/PhpInfoController.php b/src/Controllers/PhpInfoController.php
index 289e03b686..762c7b90e3 100644
--- a/src/Controllers/PhpInfoController.php
+++ b/src/Controllers/PhpInfoController.php
@@ -1,17 +1,18 @@
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);
}
}
diff --git a/src/Controllers/Preferences/ManageController.php b/src/Controllers/Preferences/ManageController.php
index 355359f253..fb7878d681 100644
--- a/src/Controllers/Preferences/ManageController.php
+++ b/src/Controllers/Preferences/ManageController.php
@@ -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')) {
diff --git a/src/Controllers/SchemaExportController.php b/src/Controllers/SchemaExportController.php
index e9c8fda199..ac23784550 100644
--- a/src/Controllers/SchemaExportController.php
+++ b/src/Controllers/SchemaExportController.php
@@ -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']);
}
}
diff --git a/src/Controllers/Table/GetFieldController.php b/src/Controllers/Table/GetFieldController.php
index e23cc4ca3e..e49b9a08a2 100644
--- a/src/Controllers/Table/GetFieldController.php
+++ b/src/Controllers/Table/GetFieldController.php
@@ -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);
}
}
diff --git a/src/Controllers/Table/GisVisualizationController.php b/src/Controllers/Table/GisVisualizationController.php
index 6b9be5223b..281187fffb 100644
--- a/src/Controllers/Table/GisVisualizationController.php
+++ b/src/Controllers/Table/GisVisualizationController.php
@@ -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']);
diff --git a/src/Controllers/Table/TrackingController.php b/src/Controllers/Table/TrackingController.php
index a4f2143b05..0f49fdb0ef 100644
--- a/src/Controllers/Table/TrackingController.php
+++ b/src/Controllers/Table/TrackingController.php
@@ -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
diff --git a/src/Controllers/Transformation/WrapperController.php b/src/Controllers/Transformation/WrapperController.php
index 01e26f3197..c728110936 100644
--- a/src/Controllers/Transformation/WrapperController.php
+++ b/src/Controllers/Transformation/WrapperController.php
@@ -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
diff --git a/src/Controllers/VersionCheckController.php b/src/Controllers/VersionCheckController.php
index 38c116cae5..cfed961ec8 100644
--- a/src/Controllers/VersionCheckController.php
+++ b/src/Controllers/VersionCheckController.php
@@ -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]));
}
}
diff --git a/src/Http/Middleware/UrlRedirection.php b/src/Http/Middleware/UrlRedirection.php
index 056a963d45..76a7e6c190 100644
--- a/src/Http/Middleware/UrlRedirection.php
+++ b/src/Http/Middleware/UrlRedirection.php
@@ -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);
}
}
diff --git a/src/ResponseRenderer.php b/src/ResponseRenderer.php
index 6231f5090d..7cf1903494 100644
--- a/src/ResponseRenderer.php
+++ b/src/ResponseRenderer.php
@@ -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);
}
diff --git a/src/UrlRedirector.php b/src/UrlRedirector.php
index 6fdf4c7a1d..96e17c2b4c 100644
--- a/src/UrlRedirector.php
+++ b/src/UrlRedirector.php
@@ -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]));
}
}
diff --git a/tests/unit/Controllers/ChangeLogControllerTest.php b/tests/unit/Controllers/ChangeLogControllerTest.php
index 95c3c7bff8..178110206b 100644
--- a/tests/unit/Controllers/ChangeLogControllerTest.php
+++ b/tests/unit/Controllers/ChangeLogControllerTest.php
@@ -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)
-- issue #17522 Fix case where the routes cache file is invalid
-- issue Upgrade slim/psr7 to 1.4.1 for CVE-2023-30536 - GHSA-q2qj-628g-vhfw
+ 5.2.2 (not yet released)
+ - issue #17522 Fix case where the routes cache file is invalid
+ - issue Upgrade slim/psr7 to 1.4.1 for CVE-2023-30536 - GHSA-q2qj-628g-vhfw
-5.2.1 (2023-02-07)
-- issue #16418 Fix FAQ 1.44 about manually removing vendor folders
-- issue [security] Fix an XSS attack through the drag-and-drop upload feature (PMASA-2023-01)
+ 5.2.1 (2023-02-07)
+ - issue #16418 Fix FAQ 1.44 about manually removing vendor folders
+ - issue [security] Fix an XSS attack through the drag-and-drop upload feature (PMASA-2023-01)
- --- Older ChangeLogs can be found on our project website ---
- https://www.phpmyadmin.net/old-stuff/ChangeLogs/
+ --- Older ChangeLogs can be found on our project website ---
+ https://www.phpmyadmin.net/old-stuff/ChangeLogs/
-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(
'- '
. 'issue #16418 Fix '
. 'FAQ 1.44 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'
. ' phpmyadmin.net for more information.',
- self::getActualOutputForAssertion(),
+ (string) $response->getBody(),
);
}
}
diff --git a/tests/unit/Controllers/Database/Structure/AddPrefixControllerTest.php b/tests/unit/Controllers/Database/Structure/AddPrefixControllerTest.php
new file mode 100644
index 0000000000..14fee1a563
--- /dev/null
+++ b/tests/unit/Controllers/Database/Structure/AddPrefixControllerTest.php
@@ -0,0 +1,42 @@
+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());
+ }
+}
diff --git a/tests/unit/Controllers/Database/Structure/ChangePrefixFormControllerTest.php b/tests/unit/Controllers/Database/Structure/ChangePrefixFormControllerTest.php
new file mode 100644
index 0000000000..fbc540a871
--- /dev/null
+++ b/tests/unit/Controllers/Database/Structure/ChangePrefixFormControllerTest.php
@@ -0,0 +1,42 @@
+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());
+ }
+}
diff --git a/tests/unit/Controllers/Database/Structure/CopyFormControllerTest.php b/tests/unit/Controllers/Database/Structure/CopyFormControllerTest.php
new file mode 100644
index 0000000000..5280d109d8
--- /dev/null
+++ b/tests/unit/Controllers/Database/Structure/CopyFormControllerTest.php
@@ -0,0 +1,56 @@
+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();
+ }
+}
diff --git a/tests/unit/Controllers/LicenseControllerTest.php b/tests/unit/Controllers/LicenseControllerTest.php
new file mode 100644
index 0000000000..d5b2eaed0a
--- /dev/null
+++ b/tests/unit/Controllers/LicenseControllerTest.php
@@ -0,0 +1,32 @@
+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);
+ }
+}
diff --git a/tests/unit/Controllers/LintControllerTest.php b/tests/unit/Controllers/LintControllerTest.php
index 90b41fa07a..5440ccb18c 100644
--- a/tests/unit/Controllers/LintControllerTest.php
+++ b/tests/unit/Controllers/LintControllerTest.php
@@ -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());
}
}
diff --git a/tests/unit/Controllers/PhpInfoControllerTest.php b/tests/unit/Controllers/PhpInfoControllerTest.php
new file mode 100644
index 0000000000..5d157e4633
--- /dev/null
+++ b/tests/unit/Controllers/PhpInfoControllerTest.php
@@ -0,0 +1,60 @@
+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'));
+ }
+}
diff --git a/tests/unit/Controllers/SchemaExportControllerTest.php b/tests/unit/Controllers/SchemaExportControllerTest.php
index 1f3f7a1e63..3b8b6633aa 100644
--- a/tests/unit/Controllers/SchemaExportControllerTest.php
+++ b/tests/unit/Controllers/SchemaExportControllerTest.php
@@ -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);
}
}
diff --git a/tests/unit/Controllers/Table/GetFieldControllerTest.php b/tests/unit/Controllers/Table/GetFieldControllerTest.php
index ec9f7173d0..62bdb74373 100644
--- a/tests/unit/Controllers/Table/GetFieldControllerTest.php
+++ b/tests/unit/Controllers/Table/GetFieldControllerTest.php
@@ -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());
}
}
diff --git a/tests/unit/Controllers/Table/GisVisualizationControllerTest.php b/tests/unit/Controllers/Table/GisVisualizationControllerTest.php
index 20feac71c4..5835cbd61a 100644
--- a/tests/unit/Controllers/Table/GisVisualizationControllerTest.php
+++ b/tests/unit/Controllers/Table/GisVisualizationControllerTest.php
@@ -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());
}
}
diff --git a/tests/unit/Controllers/Table/TrackingControllerTest.php b/tests/unit/Controllers/Table/TrackingControllerTest.php
index 952edb783b..2231edf179 100644
--- a/tests/unit/Controllers/Table/TrackingControllerTest.php
+++ b/tests/unit/Controllers/Table/TrackingControllerTest.php
@@ -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());
}
}
diff --git a/tests/unit/Controllers/VersionCheckControllerTest.php b/tests/unit/Controllers/VersionCheckControllerTest.php
index 21d2f7d609..f0ea438c92 100644
--- a/tests/unit/Controllers/VersionCheckControllerTest.php
+++ b/tests/unit/Controllers/VersionCheckControllerTest.php
@@ -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());
}
}
diff --git a/tests/unit/Stubs/ResponseRenderer.php b/tests/unit/Stubs/ResponseRenderer.php
index e2ed1c687a..c4804bf15d 100644
--- a/tests/unit/Stubs/ResponseRenderer.php
+++ b/tests/unit/Stubs/ResponseRenderer.php
@@ -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;
diff --git a/tests/unit/UrlRedirectorTest.php b/tests/unit/UrlRedirectorTest.php
index 577261e86f..862673c510 100644
--- a/tests/unit/UrlRedirectorTest.php
+++ b/tests/unit/UrlRedirectorTest.php
@@ -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'
-
-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'
+
+ Taking you to the target site.
+
+ HTML;
+
+ self::assertSame($expected, (string) $response->getBody());
}
}