Skip to content

Commit

Permalink
Wrap the Application::run() code with a HTTP handler
Browse files Browse the repository at this point in the history
- Uses laminas-httphandlerrunner to emit the Response object

Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
  • Loading branch information
MauricioFauth committed Jul 27, 2023
1 parent 0b9db09 commit ecf1c78
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 57 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"composer/ca-bundle": "^1.2",
"fig/http-message-util": "^1.1",
"google/recaptcha": "^1.3",
"laminas/laminas-httphandlerrunner": "^2.6",
"nikic/fast-route": "^1.3",
"phpmyadmin/motranslator": "^5.0",
"phpmyadmin/shapefile": "^3.0.1",
Expand Down
91 changes: 37 additions & 54 deletions libraries/classes/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace PhpMyAdmin;

use Fig\Http\Message\StatusCodeInterface;
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
use PhpMyAdmin\Config\ConfigFile;
use PhpMyAdmin\Config\Settings\Server;
use PhpMyAdmin\ConfigStorage\Relation;
Expand All @@ -12,7 +14,9 @@
use PhpMyAdmin\Exceptions\ConfigException;
use PhpMyAdmin\Exceptions\MissingExtensionException;
use PhpMyAdmin\Exceptions\SessionHandlerException;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\TableName;
Expand Down Expand Up @@ -57,6 +61,7 @@ public function __construct(
private readonly ErrorHandler $errorHandler,
private readonly Config $config,
private readonly Template $template,
private readonly ResponseFactory $responseFactory,
) {
}

Expand All @@ -68,36 +73,22 @@ public static function init(): self
return $application;
}

/**
* Misc stuff and REQUIRED by ALL the scripts.
* MUST be included by every script
*
* Among other things, it contains the advanced authentication work.
*
* Order of sections:
*
* the authentication libraries must be before the connection to db
*
* ... so the required order is:
*
* LABEL_variables_init
* - initialize some variables always needed
* LABEL_parsing_config_file
* - parsing of the configuration file
* LABEL_loading_language_file
* - loading language file
* LABEL_setup_servers
* - check and setup configured servers
* LABEL_theme_setup
* - setting up themes
*
* - load of MySQL extension (if necessary)
* - loading of an authentication library
* - db connection
* - authentication work
*/
public function run(bool $isSetupPage = false): void
{
$request = self::getRequest()->withAttribute('isSetupPage', $isSetupPage);
$response = $this->handle($request);
if ($response === null) {
return;
}

$emitter = new SapiEmitter();
$emitter->emit($response);
}

private function handle(ServerRequest $request): Response|null
{
$isSetupPage = (bool) $request->getAttribute('isSetupPage');

$GLOBALS['errorHandler'] = $this->errorHandler;
$GLOBALS['config'] = $this->config;

Expand All @@ -106,9 +97,8 @@ public function run(bool $isSetupPage = false): void
} catch (MissingExtensionException $exception) {
// Disables template caching because the cache directory is not known yet.
$this->template->disableCache();
echo $this->getGenericError($exception->getMessage());

return;
return $this->getGenericErrorResponse($exception->getMessage());
}

$this->configurePhpSettings();
Expand All @@ -118,12 +108,10 @@ public function run(bool $isSetupPage = false): void
} catch (ConfigException $exception) {
// Disables template caching because the cache directory is not known yet.
$this->template->disableCache();
echo $this->getGenericError($exception->getMessage());

return;
return $this->getGenericErrorResponse($exception->getMessage());
}

$request = self::getRequest();
$route = $request->getRoute();

$isMinimumCommon = $isSetupPage || $route === '/import-status' || $route === '/url' || $route === '/messages';
Expand All @@ -135,9 +123,7 @@ public function run(bool $isSetupPage = false): void
// Include session handling after the globals, to prevent overwriting.
Session::setUp($this->config, $this->errorHandler);
} catch (SessionHandlerException $exception) {
echo $this->getGenericError($exception->getMessage());

return;
return $this->getGenericErrorResponse($exception->getMessage());
}
}

Expand Down Expand Up @@ -182,18 +168,14 @@ public function run(bool $isSetupPage = false): void
$this->config->checkPermissions();
$this->config->checkErrors();
} catch (ConfigException $exception) {
echo $this->getGenericError($exception->getMessage());

return;
return $this->getGenericErrorResponse($exception->getMessage());
}

try {
$this->checkServerConfiguration();
$this->checkRequest();
} catch (RuntimeException $exception) {
echo $this->getGenericError($exception->getMessage());

return;
return $this->getGenericErrorResponse($exception->getMessage());
}

$this->setCurrentServerGlobal($container, $this->config, $request->getParam('server'));
Expand All @@ -219,12 +201,12 @@ public function run(bool $isSetupPage = false): void
$this->setupPageBootstrap($this->config);
Routing::callSetupController($request);

return;
return null;
}

Routing::callControllerForRoute($request, Routing::getDispatcher(), $container);

return;
return null;
}

/**
Expand All @@ -247,9 +229,7 @@ public function run(bool $isSetupPage = false): void
try {
$authPlugin = $authPluginFactory->create();
} catch (AuthenticationPluginException $exception) {
echo $this->getGenericError($exception->getMessage());

return;
return $this->getGenericErrorResponse($exception->getMessage());
}

$authPlugin->authenticate();
Expand All @@ -271,13 +251,11 @@ public function run(bool $isSetupPage = false): void
Logging::logUser($this->config, $currentServer->user);

if ($GLOBALS['dbi']->getVersion() < $settings->mysqlMinVersion['internal']) {
echo $this->getGenericError(sprintf(
return $this->getGenericErrorResponse(sprintf(
__('You should upgrade to %s %s or later.'),
'MySQL',
$settings->mysqlMinVersion['human'],
));

return;
}

/** @var mixed $sqlDelimiter */
Expand Down Expand Up @@ -309,7 +287,7 @@ public function run(bool $isSetupPage = false): void
Message::error(__('Error: Token mismatch')),
);

return;
return null;
}

Profiling::check($GLOBALS['dbi'], $response);
Expand All @@ -329,6 +307,8 @@ public function run(bool $isSetupPage = false): void
}

Routing::callControllerForRoute($request, Routing::getDispatcher(), $container);

return null;
}

/**
Expand Down Expand Up @@ -658,13 +638,16 @@ private function setCurrentServerGlobal(
$container->setParameter('url_params', $GLOBALS['urlParams']);
}

private function getGenericError(string $message): string
private function getGenericErrorResponse(string $message): Response
{
return $this->template->render('error/generic', [
$response = $this->responseFactory->createResponse(StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR);
$response->getBody()->write($this->template->render('error/generic', [
'lang' => $GLOBALS['lang'] ?? 'en',
'dir' => $GLOBALS['text_dir'] ?? 'ltr',
'error_message' => $message,
]);
]));

return $response;
}

private function updateUriScheme(Config $config, ServerRequest $request): ServerRequest
Expand Down
12 changes: 11 additions & 1 deletion libraries/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use PhpMyAdmin\Export\TemplateModel;
use PhpMyAdmin\FileListing;
use PhpMyAdmin\FlashMessages;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Import\Import;
use PhpMyAdmin\Import\SimulateDml;
use PhpMyAdmin\InsertEdit;
Expand Down Expand Up @@ -65,7 +66,12 @@
],
Application::class => [
'class' => Application::class,
'arguments' => ['$errorHandler' => '@error_handler', '$config' => '@config', '$template' => '@template'],
'arguments' => [
'$errorHandler' => '@error_handler',
'$config' => '@config',
'$template' => '@template',
'$responseFactory' => '@' . ResponseFactory::class,
],
],
'browse_foreigners' => [
'class' => BrowseForeigners::class,
Expand Down Expand Up @@ -100,6 +106,10 @@
'file_listing' => ['class' => FileListing::class],
'flash' => ['class' => FlashMessages::class],
'http_request' => ['class' => HttpRequest::class],
ResponseFactory::class => [
'class' => ResponseFactory::class,
'factory' => [ResponseFactory::class, 'create'],
],
'import' => ['class' => Import::class],
'import_simulate_dml' => ['class' => SimulateDml::class, 'arguments' => ['@dbi']],
'insert_edit' => [
Expand Down
11 changes: 9 additions & 2 deletions test/classes/ApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
use PhpMyAdmin\Config;
use PhpMyAdmin\ErrorHandler;
use PhpMyAdmin\Exceptions\ConfigException;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Template;
use PHPUnit\Framework\Attributes\BackupStaticProperties;
use PHPUnit\Framework\Attributes\CoversClass;
use Psr\Http\Message\ResponseFactoryInterface;
use ReflectionProperty;
use Symfony\Component\DependencyInjection\ContainerBuilder;

Expand All @@ -23,6 +26,7 @@ public function testInit(): void
$this->createStub(ErrorHandler::class),
$this->createStub(Config::class),
$this->createStub(Template::class),
new ResponseFactory($this->createStub(ResponseFactoryInterface::class)),
);
$container = $this->createMock(ContainerBuilder::class);
$container->expects($this->once())->method('get')
Expand All @@ -31,6 +35,7 @@ public function testInit(): void
$this->assertSame($application, Application::init());
}

#[BackupStaticProperties(true)]
public function testRunWithConfigError(): void
{
$GLOBALS['errorHandler'] = null;
Expand All @@ -41,7 +46,8 @@ public function testRunWithConfigError(): void
$config->expects($this->once())->method('loadAndCheck')
->willThrowException(new ConfigException('Failed to load phpMyAdmin configuration.'));

$request = $this->createStub(ServerRequest::class);
$request = $this->createMock(ServerRequest::class);
$request->expects($this->once())->method('withAttribute')->willReturnSelf();
(new ReflectionProperty(Application::class, 'request'))->setValue(null, $request);

$template = new Template($config);
Expand All @@ -51,7 +57,7 @@ public function testRunWithConfigError(): void
'error_message' => 'Failed to load phpMyAdmin configuration.',
]);

$application = new Application($errorHandler, $config, $template);
$application = new Application($errorHandler, $config, $template, ResponseFactory::create());
$application->run();

$output = $this->getActualOutputForAssertion();
Expand All @@ -68,6 +74,7 @@ public function testCheckTokenRequestParam(): void
$this->createStub(ErrorHandler::class),
$this->createStub(Config::class),
$this->createStub(Template::class),
new ResponseFactory($this->createStub(ResponseFactoryInterface::class)),
);

$_SERVER['REQUEST_METHOD'] = 'GET';
Expand Down

0 comments on commit ecf1c78

Please sign in to comment.