diff --git a/libraries/classes/Application.php b/libraries/classes/Application.php index c35c09a75c..bccc240089 100644 --- a/libraries/classes/Application.php +++ b/libraries/classes/Application.php @@ -52,6 +52,13 @@ final class Application { private static ServerRequest|null $request = null; + public function __construct( + private readonly ErrorHandler $errorHandler, + private readonly Config $config, + private readonly Template $template, + ) { + } + public static function init(): self { /** @var Application $application */ @@ -90,20 +97,14 @@ final class Application */ public function run(bool $isSetupPage = false): void { - $container = Core::getContainerBuilder(); - - $request = self::getRequest(); - $route = $request->getRoute(); - - $isMinimumCommon = $isSetupPage || $route === '/import-status' || $route === '/url' || $route === '/messages'; - - /** @var ErrorHandler $errorHandler */ - $errorHandler = $container->get('error_handler'); - $GLOBALS['errorHandler'] = $errorHandler; + $GLOBALS['errorHandler'] = $this->errorHandler; + $GLOBALS['config'] = $this->config; try { $this->checkRequiredPhpExtensions(); } catch (MissingExtensionException $exception) { + // Disables template caching because the cache directory is not known yet. + $this->template->disableCache(); echo $this->getGenericError($exception->getMessage()); return; @@ -111,24 +112,27 @@ final class Application $this->configurePhpSettings(); - /** @var Config $config */ - $config = $container->get('config'); - $GLOBALS['config'] = $config; - try { - $config->loadAndCheck(CONFIG_FILE); + $this->config->loadAndCheck(CONFIG_FILE); } catch (ConfigException $exception) { + // Disables template caching because the cache directory is not known yet. + $this->template->disableCache(); echo $this->getGenericError($exception->getMessage()); return; } - $request = $this->updateUriScheme($config, $request); + $request = self::getRequest(); + $route = $request->getRoute(); + + $isMinimumCommon = $isSetupPage || $route === '/import-status' || $route === '/url' || $route === '/messages'; + + $request = $this->updateUriScheme($this->config, $request); if ($route !== '/messages') { try { // Include session handling after the globals, to prevent overwriting. - Session::setUp($config, $errorHandler); + Session::setUp($this->config, $this->errorHandler); } catch (SessionHandlerException $exception) { echo $this->getGenericError($exception->getMessage()); @@ -138,6 +142,8 @@ final class Application $request = Core::populateRequestWithEncryptedQueryParams($request); + $container = Core::getContainerBuilder(); + /** * init some variables LABEL_variables_init */ @@ -150,7 +156,7 @@ final class Application $GLOBALS['urlParams'] = []; $container->setParameter('url_params', $GLOBALS['urlParams']); - $this->setGotoAndBackGlobals($container, $config); + $this->setGotoAndBackGlobals($container, $this->config); $this->checkTokenRequestParam(); $this->setDatabaseAndTableFromRequest($container, $request); $this->setSQLQueryGlobalFromRequest($container, $request); @@ -172,8 +178,8 @@ final class Application * check for errors occurred while loading configuration * this check is done here after loading language files to present errors in locale */ - $config->checkPermissions(); - $config->checkErrors(); + $this->config->checkPermissions(); + $this->config->checkErrors(); } catch (ConfigException $exception) { echo $this->getGenericError($exception->getMessage()); @@ -189,10 +195,10 @@ final class Application return; } - $this->setCurrentServerGlobal($container, $config, $request->getParam('server')); + $this->setCurrentServerGlobal($container, $this->config, $request->getParam('server')); - $GLOBALS['cfg'] = $config->settings; - $settings = $config->getSettings(); + $GLOBALS['cfg'] = $this->config->settings; + $settings = $this->config->getSettings(); /** @var ThemeManager $themeManager */ $themeManager = $container->get(ThemeManager::class); @@ -201,7 +207,7 @@ final class Application $GLOBALS['dbi'] = null; if ($isMinimumCommon) { - $config->loadUserPreferences($themeManager, true); + $this->config->loadUserPreferences($themeManager, true); Tracker::enable(); if ($route === '/url') { @@ -209,7 +215,7 @@ final class Application } if ($isSetupPage) { - $this->setupPageBootstrap($config); + $this->setupPageBootstrap($this->config); Routing::callSetupController($request); return; @@ -223,7 +229,7 @@ final class Application /** * save some settings in cookies */ - $config->setCookie('pma_lang', (string) $GLOBALS['lang']); + $this->config->setCookie('pma_lang', (string) $GLOBALS['lang']); $themeManager->setThemeCookie(); @@ -231,9 +237,9 @@ final class Application $container->set(DatabaseInterface::class, $GLOBALS['dbi']); $container->setAlias('dbi', DatabaseInterface::class); - $currentServer = $config->getCurrentServer(); + $currentServer = $this->config->getCurrentServer(); if ($currentServer !== null) { - $config->getLoginCookieValidityFromCache($GLOBALS['server']); + $this->config->getLoginCookieValidityFromCache($GLOBALS['server']); /** @var AuthenticationPluginFactory $authPluginFactory */ $authPluginFactory = $container->get(AuthenticationPluginFactory::class); @@ -261,7 +267,7 @@ final class Application $authPlugin->checkTwoFactor(); /* Log success */ - Logging::logUser($config, $currentServer->user); + Logging::logUser($this->config, $currentServer->user); if ($GLOBALS['dbi']->getVersion() < $settings->mysqlMinVersion['internal']) { echo $this->getGenericError(sprintf( @@ -308,7 +314,7 @@ final class Application $container->set('response', ResponseRenderer::getInstance()); // load user preferences - $config->loadUserPreferences($themeManager); + $this->config->loadUserPreferences($themeManager); /* Tell tracker that it can actually work */ Tracker::enable(); @@ -651,7 +657,7 @@ final class Application private function getGenericError(string $message): string { - return (new Template())->render('error/generic', [ + return $this->template->render('error/generic', [ 'lang' => $GLOBALS['lang'] ?? 'en', 'dir' => $GLOBALS['text_dir'] ?? 'ltr', 'error_message' => $message, diff --git a/libraries/classes/Template.php b/libraries/classes/Template.php index dd884ad95e..a002c2065c 100644 --- a/libraries/classes/Template.php +++ b/libraries/classes/Template.php @@ -44,12 +44,11 @@ class Template public const TEMPLATES_FOLDER = ROOT_PATH . 'templates'; - private Config|null $config; + private Config $config; public function __construct(Config|null $config = null) { - $config = $config ?? $GLOBALS['config'] ?? null; - $this->config = $config instanceof Config ? $config : null; + $this->config = $config ?? $GLOBALS['config']; } public static function getTwigEnvironment(string|null $cacheDir): Environment @@ -101,7 +100,7 @@ class Template private function load(string $templateName): TemplateWrapper { if (static::$twig === null) { - static::$twig = self::getTwigEnvironment($this->config?->getTempDir('twig')); + static::$twig = self::getTwigEnvironment($this->config->getTempDir('twig')); } try { @@ -137,4 +136,13 @@ class Template { return $this->load($template)->render($data); } + + public function disableCache(): void + { + if (static::$twig === null) { + static::$twig = self::getTwigEnvironment(null); + } + + static::$twig->setCache(false); + } } diff --git a/libraries/services.php b/libraries/services.php index bf920494f7..4f889a57b4 100644 --- a/libraries/services.php +++ b/libraries/services.php @@ -63,7 +63,10 @@ return [ 'class' => Advisor::class, 'arguments' => ['$dbi' => '@dbi', '$expression' => '@expression_language'], ], - Application::class => ['class' => Application::class], + Application::class => [ + 'class' => Application::class, + 'arguments' => ['$errorHandler' => '@error_handler', '$config' => '@config', '$template' => '@template'], + ], 'browse_foreigners' => [ 'class' => BrowseForeigners::class, 'arguments' => ['@template', '@config'], diff --git a/test/classes/ApplicationTest.php b/test/classes/ApplicationTest.php index 55b32538f8..8e13c8b6b5 100644 --- a/test/classes/ApplicationTest.php +++ b/test/classes/ApplicationTest.php @@ -19,7 +19,11 @@ final class ApplicationTest extends AbstractTestCase { public function testInit(): void { - $application = new Application(); + $application = new Application( + $this->createStub(ErrorHandler::class), + $this->createStub(Config::class), + $this->createStub(Template::class), + ); $container = $this->createMock(ContainerBuilder::class); $container->expects($this->once())->method('get') ->with($this->identicalTo(Application::class))->willReturn($application); @@ -37,23 +41,17 @@ final class ApplicationTest extends AbstractTestCase $config->expects($this->once())->method('loadAndCheck') ->willThrowException(new ConfigException('Failed to load phpMyAdmin configuration.')); - $container = $this->createStub(ContainerBuilder::class); - $container->method('get')->willReturnMap([ - ['error_handler', 1, $errorHandler], - ['config', 1, $config], - ]); - $GLOBALS['containerBuilder'] = $container; - $request = $this->createStub(ServerRequest::class); (new ReflectionProperty(Application::class, 'request'))->setValue($request); - $expected = (new Template())->render('error/generic', [ + $template = new Template($config); + $expected = $template->render('error/generic', [ 'lang' => 'en', 'dir' => 'ltr', 'error_message' => 'Failed to load phpMyAdmin configuration.', ]); - $application = new Application(); + $application = new Application($errorHandler, $config, $template); $application->run(); $output = $this->getActualOutputForAssertion(); @@ -66,7 +64,11 @@ final class ApplicationTest extends AbstractTestCase public function testCheckTokenRequestParam(): void { - $application = new Application(); + $application = new Application( + $this->createStub(ErrorHandler::class), + $this->createStub(Config::class), + $this->createStub(Template::class), + ); $_SERVER['REQUEST_METHOD'] = 'GET'; $application->checkTokenRequestParam(); diff --git a/test/classes/TemplateTest.php b/test/classes/TemplateTest.php index 0352509e22..b4a44866b2 100644 --- a/test/classes/TemplateTest.php +++ b/test/classes/TemplateTest.php @@ -11,6 +11,9 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\Attributes\RunInSeparateProcess; +use ReflectionProperty; +use Twig\Cache\CacheInterface; +use Twig\Environment; use Twig\Error\LoaderError; #[CoversClass(Template::class)] @@ -176,4 +179,19 @@ class TemplateTest extends AbstractTestCase $template2 = new Template($config); $this->assertSame('static content', $template2->render('test/static')); } + + public function testDisableCache(): void + { + (new ReflectionProperty(Template::class, 'twig'))->setValue(null); + $template = new Template($this->createStub(Config::class)); + $template->disableCache(); + $twig = (new ReflectionProperty(Template::class, 'twig'))->getValue(); + $this->assertInstanceOf(Environment::class, $twig); + $this->assertFalse($twig->getCache()); + $twig->setCache($this->createStub(CacheInterface::class)); + $this->assertNotFalse($twig->getCache()); + $template->disableCache(); + $this->assertFalse($twig->getCache()); + (new ReflectionProperty(Template::class, 'twig'))->setValue(null); + } }