diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 661ea4338a..dfd522a04d 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -12282,7 +12282,7 @@ parameters:
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
- count: 5
+ count: 4
path: src/ResponseRenderer.php
-
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 1e6a094da6..9a31f998a0 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -10159,7 +10159,6 @@
-
@@ -10173,7 +10172,6 @@
-
diff --git a/src/Header.php b/src/Header.php
index ce1498130a..a86a54c7d6 100644
--- a/src/Header.php
+++ b/src/Header.php
@@ -15,8 +15,6 @@ use PhpMyAdmin\Theme\ThemeManager;
use function array_merge;
use function defined;
-use function gmdate;
-use function header;
use function htmlspecialchars;
use function ini_get;
use function json_encode;
@@ -210,8 +208,6 @@ class Header
/** @return mixed[] */
public function getDisplay(): array
{
- $this->sendHttpHeaders();
-
$baseDir = defined('PMA_PATH_TO_BASEDIR') ? PMA_PATH_TO_BASEDIR : '';
/** @var ThemeManager $themeManager */
@@ -338,27 +334,6 @@ class Header
return $retval;
}
- /**
- * Sends out the HTTP headers
- */
- public function sendHttpHeaders(): void
- {
- if (defined('TESTSUITE')) {
- return;
- }
-
- /**
- * Sends http headers
- */
- $GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT';
-
- $headers = $this->getHttpHeaders();
-
- foreach ($headers as $name => $value) {
- header(sprintf('%s: %s', $name, $value));
- }
- }
-
/** @return array */
public function getHttpHeaders(): array
{
@@ -545,4 +520,9 @@ class Header
{
$this->isTransformationWrapper = $isTransformationWrapper;
}
+
+ public function getConsole(): Console
+ {
+ return $this->console;
+ }
}
diff --git a/src/ResponseRenderer.php b/src/ResponseRenderer.php
index a4b270c5f5..6c57c1b929 100644
--- a/src/ResponseRenderer.php
+++ b/src/ResponseRenderer.php
@@ -28,10 +28,6 @@ class ResponseRenderer
{
private static ResponseRenderer|null $instance = null;
- /**
- * Header instance
- */
- protected Header $header;
/**
* HTML data to be used in the response
*/
@@ -43,10 +39,7 @@ class ResponseRenderer
* @var mixed[]
*/
private array $JSON = [];
- /**
- * PhpMyAdmin\Footer instance
- */
- protected Footer $footer;
+
/**
* Whether we are servicing an ajax request.
*/
@@ -136,24 +129,16 @@ class ResponseRenderer
protected Response $response;
- protected Template $template;
- protected Config $config;
-
- private function __construct()
- {
- $this->config = Config::getInstance();
- $this->template = new Template();
- $dbi = DatabaseInterface::getInstance();
- $relation = new Relation($dbi);
- $this->header = new Header(
- $this->template,
- new Console($relation, $this->template, new BookmarkRepository($dbi, $relation)),
- $this->config,
- );
- $this->footer = new Footer($this->template, $this->config);
- $this->response = ResponseFactory::create()->createResponse();
-
- $this->setAjax(! empty($_REQUEST['ajax_request']));
+ protected function __construct(
+ protected Config $config,
+ protected Template $template,
+ protected Header $header,
+ protected Footer $footer,
+ protected ErrorHandler $errorHandler,
+ protected DatabaseInterface $dbi,
+ ResponseFactory $responseFactory,
+ ) {
+ $this->response = $responseFactory->createResponse(StatusCodeInterface::STATUS_OK, 'OK');
}
/**
@@ -172,10 +157,26 @@ class ResponseRenderer
*/
public static function getInstance(): ResponseRenderer
{
- if (self::$instance === null) {
- self::$instance = new ResponseRenderer();
+ if (self::$instance !== null) {
+ return self::$instance;
}
+ $config = Config::getInstance();
+ $template = new Template($config);
+ $dbi = DatabaseInterface::getInstance();
+ $relation = new Relation($dbi);
+ $console = new Console($relation, $template, new BookmarkRepository($dbi, $relation));
+
+ self::$instance = new ResponseRenderer(
+ $config,
+ $template,
+ new Header($template, $console, $config),
+ new Footer($template, $config),
+ ErrorHandler::getInstance(),
+ $dbi,
+ ResponseFactory::create(),
+ );
+
return self::$instance;
}
@@ -271,7 +272,7 @@ class ResponseRenderer
$this->addJSON('title', '' . $this->getHeader()->getPageTitle() . '');
}
- if (DatabaseInterface::getInstance()->isConnected()) {
+ if ($this->dbi->isConnected()) {
$this->addJSON('menu', $this->getHeader()->getMenu()->getDisplay());
}
@@ -289,7 +290,7 @@ class ResponseRenderer
$this->addJSON('errors', $errors);
}
- $promptPhpErrors = ErrorHandler::getInstance()->hasErrorsForPrompt();
+ $promptPhpErrors = $this->errorHandler->hasErrorsForPrompt();
$this->addJSON('promptPhpErrors', $promptPhpErrors);
if (empty($GLOBALS['error_message'])) {
@@ -317,12 +318,6 @@ class ResponseRenderer
}
}
- // Set the Content-Type header to JSON so that jQuery parses the
- // response correctly.
- foreach (Core::headerJSON() as $name => $value) {
- $this->addHeader($name, $value);
- }
-
$result = json_encode($this->JSON);
if ($result === false) {
return (string) json_encode([
@@ -336,9 +331,19 @@ class ResponseRenderer
public function response(): Response
{
- $this->response->getBody()->write($this->isAjax() ? $this->ajaxResponse() : $this->getDisplay());
+ if ($this->isAjax()) {
+ $headers = Core::headerJSON();
+ $body = $this->ajaxResponse();
+ } else {
+ $headers = $this->header->getHttpHeaders();
+ $body = $this->getDisplay();
+ }
- return $this->response;
+ foreach ($headers as $name => $value) {
+ $this->response = $this->response->withHeader($name, $value);
+ }
+
+ return $this->response->write($body);
}
public function addHeader(string $name, string $value): void
diff --git a/tests/unit/ResponseRendererTest.php b/tests/unit/ResponseRendererTest.php
index cc639cac8e..7b3abb1de0 100644
--- a/tests/unit/ResponseRendererTest.php
+++ b/tests/unit/ResponseRendererTest.php
@@ -5,19 +5,24 @@ declare(strict_types=1);
namespace PhpMyAdmin\Tests;
use Fig\Http\Message\StatusCodeInterface;
+use PhpMyAdmin\Config;
+use PhpMyAdmin\Current;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Header;
use PhpMyAdmin\Html\MySQLDocumentation;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Scripts;
+use PhpMyAdmin\Template;
+use PhpMyAdmin\Version;
use PHPUnit\Framework\Attributes\CoversClass;
use ReflectionProperty;
use function array_column;
+use function json_decode;
#[CoversClass(ResponseRenderer::class)]
-class ResponseRendererTest extends AbstractTestCase
+final class ResponseRendererTest extends AbstractTestCase
{
protected function setUp(): void
{
@@ -107,4 +112,115 @@ class ResponseRendererTest extends AbstractTestCase
(new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
}
+
+ public function testHtmlResponse(): void
+ {
+ (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
+
+ $_SERVER['SCRIPT_NAME'] = 'index.php';
+ Current::$server = 0;
+
+ $responseRenderer = ResponseRenderer::getInstance();
+ $responseRenderer->setAjax(false);
+ $responseRenderer->addHTML('TEST
');
+
+ $response = $responseRenderer->response();
+
+ self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
+ self::assertSame('text/html; charset=utf-8', $response->getHeaderLine('Content-Type'));
+ $header = $responseRenderer->getHeader();
+ self::assertSame(
+ (new Template(new Config()))->render('base', [
+ 'header' => [
+ 'lang' => 'en',
+ 'allow_third_party_framing' => false,
+ 'base_dir' => '',
+ 'theme_path' => '',
+ 'version' => 'v=' . Version::VERSION,
+ 'text_dir' => 'ltr',
+ 'server' => 0,
+ 'title' => 'phpMyAdmin',
+ 'scripts' => $header->getScripts()->getDisplay(),
+ 'body_id' => '',
+ 'navigation' => '',
+ 'custom_header' => '',
+ 'load_user_preferences' => '',
+ 'show_hint' => true,
+ 'is_warnings_enabled' => true,
+ 'is_menu_enabled' => true,
+ 'is_logged_in' => true,
+ 'menu' => '',
+ 'console' => $header->getConsole()->getDisplay(),
+ 'messages' => '',
+ 'theme_color_mode' => 'light',
+ 'theme_color_modes' => ['light'],
+ 'theme_id' => '',
+ 'current_user' => ['pma_test', 'localhost'],
+ 'is_mariadb' => false,
+ ],
+ 'content' => 'TEST
',
+ 'footer' => [
+ 'is_minimal' => false,
+ 'self_url' => 'index.php?route=%2F&server=0&lang=en',
+ 'error_messages' => '',
+ 'scripts' => <<<'HTML'
+
+
+
+ HTML,
+ 'is_demo' => false,
+ 'git_revision_info' => [],
+ 'footer' => '',
+ ],
+ ]),
+ (string) $response->getBody(),
+ );
+
+ (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
+ }
+
+ public function testJsonResponse(): void
+ {
+ (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
+
+ $_SERVER['SCRIPT_NAME'] = 'index.php';
+ Current::$server = 0;
+
+ $responseRenderer = ResponseRenderer::getInstance();
+ $responseRenderer->setAjax(true);
+ $responseRenderer->addJSON('message', 'test message');
+ $responseRenderer->addJSON('test', 'test');
+
+ $response = $responseRenderer->response();
+
+ self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());
+ self::assertSame('application/json; charset=UTF-8', $response->getHeaderLine('Content-Type'));
+ $body = (string) $response->getBody();
+ self::assertJson($body);
+ $header = $responseRenderer->getHeader();
+ self::assertEquals(
+ [
+ 'message' => 'test message',
+ 'test' => 'test',
+ 'success' => true,
+ 'title' => 'phpMyAdmin',
+ 'menu' => $header->getMenu()->getDisplay(),
+ 'scripts' => $header->getScripts()->getFiles(),
+ 'selflink' => 'index.php?route=%2F&server=0&lang=en',
+ 'displayMessage' => '',
+ 'debug' => "'false'",
+ 'promptPhpErrors' => false,
+ 'reloadQuerywindow' => ['db' => '', 'table' => '', 'sql_query' => ''],
+ 'params' => $header->getJsParams(),
+ ],
+ json_decode($body, true),
+ );
+
+ (new ReflectionProperty(ResponseRenderer::class, 'instance'))->setValue(null, null);
+ }
}
diff --git a/tests/unit/Stubs/ResponseRenderer.php b/tests/unit/Stubs/ResponseRenderer.php
index d51daab7d3..e073928eca 100644
--- a/tests/unit/Stubs/ResponseRenderer.php
+++ b/tests/unit/Stubs/ResponseRenderer.php
@@ -16,6 +16,7 @@ use PhpMyAdmin\Config;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Console;
use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\Error\ErrorHandler;
use PhpMyAdmin\Footer;
use PhpMyAdmin\Header;
use PhpMyAdmin\Http\Factory\ResponseFactory;
@@ -46,24 +47,25 @@ class ResponseRenderer extends \PhpMyAdmin\ResponseRenderer
*/
public function __construct()
{
- $this->isSuccess = true;
- $this->isAjax = false;
-
$GLOBALS['lang'] = 'en';
- $this->template = new Template();
- $this->config = Config::getInstance();
- $this->config->selectedServer['pmadb'] = 'phpmyadmin';
+ $config = Config::getInstance();
+ $config->selectedServer['pmadb'] = 'phpmyadmin';
+ $template = new Template($config);
$dummyDbi = new DbiDummy();
$dummyDbi->addSelectDb('phpmyadmin');
$dbi = new DatabaseInterface($dummyDbi);
$relation = new Relation($dbi);
- $this->header = new Header(
- $this->template,
- new Console($relation, $this->template, new BookmarkRepository($dbi, $relation)),
- $this->config,
+ $console = new Console($relation, $template, new BookmarkRepository($dbi, $relation));
+
+ parent::__construct(
+ $config,
+ $template,
+ new Header($template, $console, $config),
+ new Footer($template, $config),
+ ErrorHandler::getInstance(),
+ $dbi,
+ ResponseFactory::create(),
);
- $this->footer = new Footer($this->template, $this->config);
- $this->response = ResponseFactory::create()->createResponse();
}
/**