From 7b759b46a2ad8dfc5b3dab97148b584e4e72a7e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Wed, 25 Feb 2026 15:46:24 -0300 Subject: [PATCH 1/4] Extract ResponseRenderer dependency from ReplicationGui class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- psalm-baseline.xml | 4 +++ .../Server/ReplicationController.php | 14 +++++++- src/Replication/ReplicationGui.php | 36 ++++--------------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 2ddf9fbb39..2d0515e404 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -7307,6 +7307,10 @@ + + + + diff --git a/src/Controllers/Server/ReplicationController.php b/src/Controllers/Server/ReplicationController.php index 5f56d4489b..c8b24f63ca 100644 --- a/src/Controllers/Server/ReplicationController.php +++ b/src/Controllers/Server/ReplicationController.php @@ -11,10 +11,12 @@ use PhpMyAdmin\Controllers\InvocableController; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Http\Response; use PhpMyAdmin\Http\ServerRequest; +use PhpMyAdmin\Message; use PhpMyAdmin\Replication\ReplicationGui; use PhpMyAdmin\Replication\ReplicationInfo; use PhpMyAdmin\ResponseRenderer; use PhpMyAdmin\Routing\Route; +use PhpMyAdmin\Url; use PhpMyAdmin\UrlParams; use function is_array; @@ -61,7 +63,7 @@ final class ReplicationController implements InvocableController $srSkipErrorsCount = $request->getParsedBodyParamAsStringOrNull('sr_skip_errors_count', '1'); $srReplicaControlParam = $request->getParsedBodyParamAsStringOrNull('sr_replica_control_param'); - $this->replicationGui->handleControlRequest( + $message = $this->replicationGui->handleControlRequest( $request->getParsedBodyParam('sr_take_action') !== null, $request->getParsedBodyParam('replica_changeprimary') !== null, $request->getParsedBodyParam('sr_replica_server_control') !== null, @@ -74,6 +76,16 @@ final class ReplicationController implements InvocableController $request->getParsedBodyParamAsString('hostname', ''), (int) $request->getParsedBodyParamAsStringOrNull('text_port'), ); + if ($message instanceof Message) { + if ($request->isAjax()) { + $this->response->setRequestStatus($message->isSuccess()); + $this->response->addJSON('message', $message); + } else { + $this->response->redirect( + './index.php?route=/server/replication' . Url::getCommonRaw(UrlParams::$params, '&'), + ); + } + } } $errorMessages = $this->replicationGui->getHtmlForErrorMessage(); diff --git a/src/Replication/ReplicationGui.php b/src/Replication/ReplicationGui.php index b373c5e6a2..8bbc800d92 100644 --- a/src/Replication/ReplicationGui.php +++ b/src/Replication/ReplicationGui.php @@ -12,7 +12,6 @@ use PhpMyAdmin\Dbal\ConnectionType; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Message; use PhpMyAdmin\Query\Utilities; -use PhpMyAdmin\ResponseRenderer; use PhpMyAdmin\Template; use PhpMyAdmin\Url; use PhpMyAdmin\UrlParams; @@ -425,16 +424,11 @@ class ReplicationGui string $pmaPassword, string $hostname, int $port, - ): void { + ): Message|null { if (! $srTakeAction) { - return; + return null; } - $refresh = false; - $result = false; - $messageSuccess = ''; - $messageError = ''; - if ($replicaChangePrimary && ! Config::getInstance()->settings['AllowArbitraryServer']) { $_SESSION['replication']['sr_action_status'] = 'error'; $_SESSION['replication']['sr_action_info'] = __( @@ -442,10 +436,9 @@ class ReplicationGui . ' $cfg[\'AllowArbitraryServer\'] in phpMyAdmin configuration.', ); } elseif ($replicaChangePrimary) { - $result = $this->handleRequestForReplicaChangePrimary($username, $pmaPassword, $hostname, $port); + $this->handleRequestForReplicaChangePrimary($username, $pmaPassword, $hostname, $port); } elseif ($srReplicaServerControl) { $result = $this->handleRequestForReplicaServerControl($srReplicaAction, $srReplicaControlParam); - $refresh = true; switch ($srReplicaAction) { case 'start': @@ -465,28 +458,13 @@ class ReplicationGui $messageError = __('Error.'); break; } + + return $result ? Message::success($messageSuccess) : Message::error($messageError); } elseif ($srReplicaSkipError) { - $result = $this->handleRequestForReplicaSkipError($srSkipErrorsCount); + $this->handleRequestForReplicaSkipError($srSkipErrorsCount); } - if ($refresh) { - $response = ResponseRenderer::getInstance(); - if ($response->isAjax()) { - $response->setRequestStatus($result); - $response->addJSON( - 'message', - $result - ? Message::success($messageSuccess) - : Message::error($messageError), - ); - } else { - $response->redirect( - './index.php?route=/server/replication' . Url::getCommonRaw(UrlParams::$params, '&'), - ); - } - } - - unset($refresh); + return null; } public function handleRequestForReplicaChangePrimary( From 495a4d031ece5cf872a5706a5f33ae4d1b80eb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Thu, 26 Feb 2026 15:34:21 -0300 Subject: [PATCH 2/4] Extract ResponseRenderer dependency from Privileges class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- phpstan-baseline.neon | 4 +- psalm-baseline.xml | 1 - .../Server/PrivilegesController.php | 26 ++++++++-- src/Exceptions/UpdateAuthPluginFailure.php | 11 ++++ src/Server/Privileges.php | 51 ++++++------------- tests/unit/Server/PrivilegesTest.php | 10 ++-- 6 files changed, 54 insertions(+), 49 deletions(-) create mode 100644 src/Exceptions/UpdateAuthPluginFailure.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 07f46e26d6..b0911321b6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -11043,7 +11043,7 @@ parameters: - message: '#^Construct empty\(\) is not allowed\. Use more strict comparison\.$#' identifier: empty.notAllowed - count: 13 + count: 12 path: src/Server/Privileges.php - @@ -11079,7 +11079,7 @@ parameters: - message: '#^Only booleans are allowed in a negated boolean, PhpMyAdmin\\Dbal\\ResultInterface\|false given\.$#' identifier: booleanNot.exprNotBoolean - count: 13 + count: 11 path: src/Server/Privileges.php - diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 2d0515e404..64435116d4 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -7555,7 +7555,6 @@ - diff --git a/src/Controllers/Server/PrivilegesController.php b/src/Controllers/Server/PrivilegesController.php index f69aaac6b6..92de55b47d 100644 --- a/src/Controllers/Server/PrivilegesController.php +++ b/src/Controllers/Server/PrivilegesController.php @@ -10,6 +10,8 @@ use PhpMyAdmin\ConfigStorage\RelationCleanup; use PhpMyAdmin\Controllers\InvocableController; use PhpMyAdmin\Current; use PhpMyAdmin\Dbal\DatabaseInterface; +use PhpMyAdmin\Exceptions\UpdateAuthPluginFailure; +use PhpMyAdmin\Exceptions\UserPasswordUpdateFailure; use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Http\Response; use PhpMyAdmin\Http\ServerRequest; @@ -262,11 +264,23 @@ final class PrivilegesController implements InvocableController * Updates the password */ if ($request->hasBodyParam('change_pw')) { - Current::$message = $serverPrivileges->updatePassword( - $errorUrl, - $serverPrivileges->username ?? '', - $serverPrivileges->hostname ?? '', - ); + try { + Current::$message = $serverPrivileges->updatePassword( + $serverPrivileges->username ?? '', + $serverPrivileges->hostname ?? '', + ); + } catch (UpdateAuthPluginFailure | UserPasswordUpdateFailure $exception) { + if ($request->isAjax()) { + $this->response->setRequestStatus(false); + $this->response->addJSON('message', $exception->getMessage()); + + return $this->response->response(); + } + + $this->response->addHTML($exception->getMessage() . Generator::getBackUrlHtml($errorUrl)); + + return $this->response->response(); + } } /** @@ -392,6 +406,8 @@ final class PrivilegesController implements InvocableController $this->response->addHTML($serverPrivileges->getHtmlForUserOverview( $userPrivileges, $request->getQueryParam('initial'), + $request->isAjax(), + $request->has('ajax_page_request'), )); } elseif ($routinename !== '') { $this->response->addHTML( diff --git a/src/Exceptions/UpdateAuthPluginFailure.php b/src/Exceptions/UpdateAuthPluginFailure.php new file mode 100644 index 0000000000..317657eb2f --- /dev/null +++ b/src/Exceptions/UpdateAuthPluginFailure.php @@ -0,0 +1,11 @@ +getUserHostCondition($username, $hostname) . ';'; // Update the plugin for the user - if (! $this->dbi->tryQuery($updatePluginQuery)) { - $errorMessage = Generator::mysqlDie( + if ($this->dbi->tryQuery($updatePluginQuery) === false) { + throw new UpdateAuthPluginFailure(Generator::mysqlDie( $this->dbi->getError(), $updatePluginQuery, false, - ); - - $response = ResponseRenderer::getInstance(); - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $errorMessage); - $response->callExit(); - } - - $response->addHTML($errorMessage . Generator::getBackUrlHtml($errorUrl)); - $response->callExit(); + )); } $this->dbi->tryQuery('FLUSH PRIVILEGES;'); @@ -886,22 +874,12 @@ class Privileges . '(' . $this->dbi->quoteString($_POST['pma_pw']) . ')'); } - if (! $this->dbi->tryQuery($localQuery)) { - $errorMessage = Generator::mysqlDie( + if ($this->dbi->tryQuery($localQuery) === false) { + throw new UserPasswordUpdateFailure(Generator::mysqlDie( $this->dbi->getError(), $sqlQuery, false, - ); - - $response = ResponseRenderer::getInstance(); - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $errorMessage); - $response->callExit(); - } - - $response->addHTML($errorMessage . Generator::getBackUrlHtml($errorUrl)); - $response->callExit(); + )); } // Flush privileges after successful password change @@ -2456,8 +2434,12 @@ class Privileges /** * Get HTML snippet for display user overview page */ - public function getHtmlForUserOverview(UserPrivileges $userPrivileges, string|null $initial): string - { + public function getHtmlForUserOverview( + UserPrivileges $userPrivileges, + string|null $initial, + bool $isAjax, + bool $isAjaxPageRequest, + ): string { $serverVersion = $this->dbi->getVersion(); $passwordColumn = Compatibility::isMySqlOrPerconaDb($this->dbi) && $serverVersion >= 50706 ? 'authentication_string' @@ -2481,8 +2463,7 @@ class Privileges $this->template->render('export_modal'); } - $response = ResponseRenderer::getInstance(); - if (! $response->isAjax() || ! empty($_REQUEST['ajax_page_request'])) { + if (! $isAjax || $isAjaxPageRequest) { if ($userPrivileges->isReload) { $flushnote = new Message( __( diff --git a/tests/unit/Server/PrivilegesTest.php b/tests/unit/Server/PrivilegesTest.php index 333a6b6ebe..a6306e5983 100644 --- a/tests/unit/Server/PrivilegesTest.php +++ b/tests/unit/Server/PrivilegesTest.php @@ -417,11 +417,10 @@ class PrivilegesTest extends AbstractTestCase $username = 'pma_username'; $hostname = 'pma_hostname'; - $errUrl = 'error.php'; $_POST['pma_pw'] = 'pma_pw'; $_POST['authentication_plugin'] = 'mysql_native_password'; - $message = $serverPrivileges->updatePassword($errUrl, $username, $hostname); + $message = $serverPrivileges->updatePassword($username, $hostname); self::assertSame( 'The password for \'pma_username\'@\'pma_hostname\' was changed successfully.', @@ -1570,8 +1569,7 @@ class PrivilegesTest extends AbstractTestCase $serverPrivileges = $this->getPrivileges($this->createDatabaseInterface($dummyDbi)); - $_REQUEST = ['ajax_page_request' => '1']; - $actual = $serverPrivileges->getHtmlForUserOverview($userPrivileges, null); + $actual = $serverPrivileges->getHtmlForUserOverview($userPrivileges, null, false, true); $dummyDbi->assertAllQueriesConsumed(); self::assertStringContainsString('Note: MySQL privilege names are expressed in English.', $actual); self::assertStringContainsString( @@ -1592,7 +1590,7 @@ class PrivilegesTest extends AbstractTestCase ); $dummyDbi->addResult('SELECT 1 FROM `mysql`.`user`', false); $serverPrivileges = $this->getPrivileges($this->createDatabaseInterface($dummyDbi)); - $html = $serverPrivileges->getHtmlForUserOverview($userPrivileges, null); + $html = $serverPrivileges->getHtmlForUserOverview($userPrivileges, null, false, true); self::assertStringContainsString( Url::getCommon(['adduser' => 1], ''), @@ -1620,7 +1618,7 @@ class PrivilegesTest extends AbstractTestCase ); $dummyDbi->addResult('SELECT 1 FROM `mysql`.`user`', [[1]]); $serverPrivileges = $this->getPrivileges($this->createDatabaseInterface($dummyDbi)); - $actual = $serverPrivileges->getHtmlForUserOverview($userPrivileges, null); + $actual = $serverPrivileges->getHtmlForUserOverview($userPrivileges, null, false, true); self::assertStringContainsString('Your privilege table structure seems to be older than' . ' this MySQL version!
' From 38ee763139afcc49710eba00da6667324925cb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Thu, 26 Feb 2026 15:50:54 -0300 Subject: [PATCH 3/4] Extract ResponseRenderer dependency from CreateAddField class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- src/Controllers/Table/AddFieldController.php | 9 ++---- src/CreateAddField.php | 33 ++++++-------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/src/Controllers/Table/AddFieldController.php b/src/Controllers/Table/AddFieldController.php index f4f6bf3908..9944d11f24 100644 --- a/src/Controllers/Table/AddFieldController.php +++ b/src/Controllers/Table/AddFieldController.php @@ -101,15 +101,12 @@ final readonly class AddFieldController implements InvocableController return $this->response->response(); } - $result = $createAddField->tryColumnCreationQuery( + $errorMessageHtml = $createAddField->tryColumnCreationQuery( DatabaseName::from(Current::$database), Current::$sqlQuery, - $errorUrl, ); - - if (! $result) { - $errorMessageHtml = Generator::mysqlDie('', '', false); - $this->response->addHTML($errorMessageHtml); + if ($errorMessageHtml !== null) { + $this->response->addHTML($errorMessageHtml . Generator::getBackUrlHtml($errorUrl)); $this->response->setRequestStatus(false); return $this->response->response(); diff --git a/src/CreateAddField.php b/src/CreateAddField.php index f1c7aa9e92..b640cbb32f 100644 --- a/src/CreateAddField.php +++ b/src/CreateAddField.php @@ -436,33 +436,18 @@ class CreateAddField * * @param DatabaseName $db current database * @param string $sqlQuery the query to run - * @param string $errorUrl error page url */ - public function tryColumnCreationQuery( - DatabaseName $db, - string $sqlQuery, - string $errorUrl, - ): bool { - // To allow replication, we first select the db to use and then run queries - // on this db. + public function tryColumnCreationQuery(DatabaseName $db, string $sqlQuery): string|null + { + // To allow replication, we first select the db to use and then run queries on this db. if (! $this->dbi->selectDb($db)) { - $errorMessage = Generator::mysqlDie( - $this->dbi->getError(), - 'USE ' . Util::backquote($db->getName()), - false, - ); - - $response = ResponseRenderer::getInstance(); - if ($response->isAjax()) { - $response->setRequestStatus(false); - $response->addJSON('message', $errorMessage); - $response->callExit(); - } - - $response->addHTML($errorMessage . Generator::getBackUrlHtml($errorUrl)); - $response->callExit(); + return Generator::mysqlDie($this->dbi->getError(), 'USE ' . Util::backquote($db->getName()), false); } - return (bool) $this->dbi->tryQuery($sqlQuery); + if ($this->dbi->tryQuery($sqlQuery) === false) { + return Generator::mysqlDie($this->dbi->getError(), $sqlQuery, false); + } + + return null; } } From 031775760ff05240e949269a2343c794f21858cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Thu, 26 Feb 2026 16:16:21 -0300 Subject: [PATCH 4/4] Extract ResponseRenderer dependency from Pdf class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- src/Exceptions/PdfCreationFailure.php | 11 +++++++++++ src/Pdf.php | 6 ++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 src/Exceptions/PdfCreationFailure.php diff --git a/src/Exceptions/PdfCreationFailure.php b/src/Exceptions/PdfCreationFailure.php new file mode 100644 index 0000000000..46b9d62176 --- /dev/null +++ b/src/Exceptions/PdfCreationFailure.php @@ -0,0 +1,11 @@ +getDisplay(); - ResponseRenderer::getInstance()->callExit(); + throw new PdfCreationFailure(__('Error while creating PDF:') . ' ' . $msg); } }