diff --git a/typo3/sysext/core/Documentation/Changelog/12.1/Feature-99221-AddCliInstallSetupCommand.rst b/typo3/sysext/core/Documentation/Changelog/12.1/Feature-99221-AddCliInstallSetupCommand.rst index 62ad6ec4ef6efe0a91c6b24f88ab116a78dd8e38..a9368a6ec93136bd20634ee6bf90e2a758e4a2a9 100644 --- a/typo3/sysext/core/Documentation/Changelog/12.1/Feature-99221-AddCliInstallSetupCommand.rst +++ b/typo3/sysext/core/Documentation/Changelog/12.1/Feature-99221-AddCliInstallSetupCommand.rst @@ -43,6 +43,7 @@ Automated setup: TYPO3_SETUP_ADMIN_USERNAME=admin \ TYPO3_SETUP_CREATE_SITE="https://your-typo3-site.com/" \ TYPO3_PROJECT_NAME="Automated Setup" \ + TYPO3_SERVER_TYPE="apache" \ ./bin/typo3 setup --force .. warning:: diff --git a/typo3/sysext/install/Classes/Command/SetupCommand.php b/typo3/sysext/install/Classes/Command/SetupCommand.php index b4cdac4c24bfea2209c8e6a07038b3112e6f1d84..30e03a309e0b530deaec1d8c9e9a25cd6458b106 100644 --- a/typo3/sysext/install/Classes/Command/SetupCommand.php +++ b/typo3/sysext/install/Classes/Command/SetupCommand.php @@ -35,6 +35,7 @@ use TYPO3\CMS\Install\FolderStructure\DefaultFactory; use TYPO3\CMS\Install\Service\LateBootService; use TYPO3\CMS\Install\Service\SetupDatabaseService; use TYPO3\CMS\Install\Service\SetupService; +use TYPO3\CMS\Install\WebserverType; /** * CLI command for setting up TYPO3 via CLI @@ -58,6 +59,7 @@ class SetupCommand extends Command private readonly LateBootService $lateBootService ) { parent::__construct($name); + } protected function configure() @@ -139,6 +141,13 @@ class SetupCommand extends Command 'Create a basic site setup (root page and site configuration) with the given domain', false ) + ->addOption( + 'server-type', + null, + InputOption::VALUE_OPTIONAL, + 'Define the web server the TYPO3 installation will be running on', + 'other' + ) ->addOption( 'force', null, @@ -171,6 +180,7 @@ TYPO3_SETUP_ADMIN_EMAIL=admin@example.com \ TYPO3_SETUP_ADMIN_USERNAME=admin \ TYPO3_SETUP_CREATE_SITE="https://your-typo3-site.com/" \ TYPO3_PROJECT_NAME="Automated Setup" \ +TYPO3_SERVER_TYPE="apache" \ ./bin/typo3 setup --force --------------------------------- @@ -197,8 +207,9 @@ EOT $questionHelper = $this->getHelper('question'); // Ensure all required files and folders exist + $serverType = $this->getServerType($questionHelper, $input, $output); $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $folderStructureFactory->getStructure()->fix(); + $folderStructureFactory->getStructure($serverType)->fix(); try { $force = $input->getOption('force'); @@ -479,6 +490,28 @@ EOT return $databaseConnectionOptions; } + protected function getServerType(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output): WebserverType + { + $serverTypeValidator = function (string $serverType): WebserverType { + if (!array_key_exists($serverType, WebserverType::getDescriptions())) { + throw new \RuntimeException( + 'Webserver must be any of ' . implode(', ', array_keys(WebserverType::getDescriptions())), + 1682329380, + ); + } + + return WebserverType::from($serverType); + }; + $serverTypeFromCli = $this->getFallbackValueEnvOrOption($input, 'server-type', 'TYPO3_SERVER_TYPE'); + if ($serverTypeFromCli === false && $input->isInteractive()) { + $questionServerType = new ChoiceQuestion('Which web server is used?', WebserverType::getDescriptions()); + $questionServerType->setValidator($serverTypeValidator); + return $questionHelper->ask($input, $output, $questionServerType); + } + + return $serverTypeValidator($serverTypeFromCli); + } + protected function getAdminUserName(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output): string { $usernameValidator = static function ($username) { diff --git a/typo3/sysext/install/Classes/Controller/EnvironmentController.php b/typo3/sysext/install/Classes/Controller/EnvironmentController.php index 90e21a358123a754358d192b1e542898d29cc961..3bf451be2c08ebcf0b90b769e1eb5f83231b035b 100644 --- a/typo3/sysext/install/Classes/Controller/EnvironmentController.php +++ b/typo3/sysext/install/Classes/Controller/EnvironmentController.php @@ -44,6 +44,7 @@ use TYPO3\CMS\Install\SystemEnvironment\Check; use TYPO3\CMS\Install\SystemEnvironment\DatabaseCheck; use TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck; use TYPO3\CMS\Install\SystemEnvironment\SetupCheck; +use TYPO3\CMS\Install\WebserverType; /** * Environment controller @@ -153,7 +154,7 @@ class EnvironmentController extends AbstractController { $view = $this->initializeView($request); $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $structureFacade = $folderStructureFactory->getStructure(); + $structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request)); $structureMessages = $structureFacade->getStatus(); $errorQueue = new FlashMessageQueue('install'); @@ -194,10 +195,10 @@ class EnvironmentController extends AbstractController /** * Try to fix folder structure errors */ - public function folderStructureFixAction(): ResponseInterface + public function folderStructureFixAction(ServerRequestInterface $request): ResponseInterface { $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $structureFacade = $folderStructureFactory->getStructure(); + $structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request)); $fixedStatusObjects = $structureFacade->fix(); return new JsonResponse([ 'success' => true, diff --git a/typo3/sysext/install/Classes/Controller/InstallerController.php b/typo3/sysext/install/Classes/Controller/InstallerController.php index 9d45993c2858ee6d0fce402e0962c4acd65f8fe1..cc03442b39d66ecd9c999fd8bee333ac2abb6e95 100644 --- a/typo3/sysext/install/Classes/Controller/InstallerController.php +++ b/typo3/sysext/install/Classes/Controller/InstallerController.php @@ -55,6 +55,7 @@ use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService; use TYPO3\CMS\Install\Service\SilentTemplateFileUpgradeService; use TYPO3\CMS\Install\SystemEnvironment\Check; use TYPO3\CMS\Install\SystemEnvironment\SetupCheck; +use TYPO3\CMS\Install\WebserverType; use TYPO3Fluid\Fluid\View\TemplateView as FluidTemplateView; /** @@ -151,7 +152,7 @@ final class InstallerController /** * Render "environment and folders" */ - public function showEnvironmentAndFoldersAction(): ResponseInterface + public function showEnvironmentAndFoldersAction(ServerRequestInterface $request): ResponseInterface { $view = $this->initializeView(); $systemCheckMessageQueue = new FlashMessageQueue('install'); @@ -164,7 +165,7 @@ final class InstallerController $systemCheckMessageQueue->enqueue($message); } $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $structureFacade = $folderStructureFactory->getStructure(); + $structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request)); $structureMessageQueue = $structureFacade->getStatus(); return new JsonResponse([ 'success' => true, @@ -178,10 +179,10 @@ final class InstallerController /** * Create main folder layout, LocalConfiguration, PackageStates */ - public function executeEnvironmentAndFoldersAction(): ResponseInterface + public function executeEnvironmentAndFoldersAction(ServerRequestInterface $request): ResponseInterface { $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class); - $structureFacade = $folderStructureFactory->getStructure(); + $structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request)); $structureFixMessageQueue = $structureFacade->fix(); $errorsFromStructure = $structureFixMessageQueue->getAllMessages(ContextualFeedbackSeverity::ERROR); diff --git a/typo3/sysext/install/Classes/Controller/UpgradeController.php b/typo3/sysext/install/Classes/Controller/UpgradeController.php index adfeb36c2dcdf98d1901c4578fcc1760fc2bfb11..f642b63ef4288c22bdf0ca4948f16c36637e0773 100644 --- a/typo3/sysext/install/Classes/Controller/UpgradeController.php +++ b/typo3/sysext/install/Classes/Controller/UpgradeController.php @@ -73,6 +73,7 @@ use TYPO3\CMS\Install\Service\LateBootService; use TYPO3\CMS\Install\Service\LoadTcaService; use TYPO3\CMS\Install\Service\UpgradeWizardsService; use TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile; +use TYPO3\CMS\Install\WebserverType; /** * Upgrade controller @@ -230,7 +231,10 @@ class UpgradeController extends AbstractController { $this->coreUpdateInitialize(); return new JsonResponse([ - 'success' => $this->coreUpdateService->checkPreConditions($this->coreUpdateGetVersionToHandle($request)), + 'success' => $this->coreUpdateService->checkPreConditions( + $this->coreUpdateGetVersionToHandle($request), + WebserverType::fromRequest($request), + ), 'status' => $this->coreUpdateService->getMessages(), ]); } diff --git a/typo3/sysext/install/Classes/FolderStructure/DefaultFactory.php b/typo3/sysext/install/Classes/FolderStructure/DefaultFactory.php index 4644f981ed9364738196c7e4272bba312268491d..50edb28a6e86c7543c027fc0c54e392e57bdb335 100644 --- a/typo3/sysext/install/Classes/FolderStructure/DefaultFactory.php +++ b/typo3/sysext/install/Classes/FolderStructure/DefaultFactory.php @@ -16,6 +16,7 @@ namespace TYPO3\CMS\Install\FolderStructure; use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Install\WebserverType; /** * Factory returns default folder structure object hierarchy @@ -30,9 +31,9 @@ class DefaultFactory * * @return StructureFacadeInterface */ - public function getStructure() + public function getStructure(WebserverType $webserverType) { - $rootNode = new RootNode($this->getDefaultStructureDefinition(), null); + $rootNode = new RootNode($this->getDefaultStructureDefinition($webserverType), null); return new StructureFacade($rootNode); } @@ -40,7 +41,7 @@ class DefaultFactory * Default definition of folder and file structure with dynamic * permission settings */ - protected function getDefaultStructureDefinition(): array + protected function getDefaultStructureDefinition(WebserverType $webserverType): array { $filePermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask']; $directoryPermission = $GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']; @@ -129,14 +130,14 @@ class DefaultFactory ]; // Have a default .htaccess if running apache web server or a default web.config if running IIS - if ($this->isApacheServer()) { + if ($webserverType->isApacheServer()) { $structure['children'][] = [ 'name' => '.htaccess', 'type' => FileNode::class, 'targetPermission' => $filePermission, 'targetContentFile' => self::TEMPLATE_PATH . '/root-htaccess', ]; - } elseif ($this->isMicrosoftIisServer()) { + } elseif ($webserverType->isMicrosoftInternetInformationServer()) { $structure['children'][] = [ 'name' => 'web.config', 'type' => FileNode::class, @@ -172,14 +173,14 @@ class DefaultFactory ]; // Have a default .htaccess if running apache web server or a default web.config if running IIS - if ($this->isApacheServer()) { + if ($webserverType->isApacheServer()) { $publicPathSubStructure[] = [ 'name' => '.htaccess', 'type' => FileNode::class, 'targetPermission' => $filePermission, 'targetContentFile' => self::TEMPLATE_PATH . '/root-htaccess', ]; - } elseif ($this->isMicrosoftIisServer()) { + } elseif ($webserverType->isMicrosoftInternetInformationServer()) { $publicPathSubStructure[] = [ 'name' => 'web.config', 'type' => FileNode::class, @@ -398,14 +399,4 @@ class DefaultFactory ], ]; } - - protected function isApacheServer(): bool - { - return isset($_SERVER['SERVER_SOFTWARE']) && str_starts_with($_SERVER['SERVER_SOFTWARE'], 'Apache'); - } - - protected function isMicrosoftIisServer(): bool - { - return isset($_SERVER['SERVER_SOFTWARE']) && str_starts_with($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS'); - } } diff --git a/typo3/sysext/install/Classes/Service/CoreUpdateService.php b/typo3/sysext/install/Classes/Service/CoreUpdateService.php index 89fe248789c08998863529425bada1c7d637a567..32dd02c513775bc55284966f8b6f19d07bffe933 100644 --- a/typo3/sysext/install/Classes/Service/CoreUpdateService.php +++ b/typo3/sysext/install/Classes/Service/CoreUpdateService.php @@ -25,6 +25,7 @@ use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3\CMS\Core\Utility\StringUtility; use TYPO3\CMS\Install\CoreVersion\CoreRelease; use TYPO3\CMS\Install\FolderStructure\DefaultFactory; +use TYPO3\CMS\Install\WebserverType; /** * Core update service. @@ -126,14 +127,15 @@ class CoreUpdateService * Check if an update is possible at all * * @param CoreRelease $coreRelease The target core release + * @param WebserverType $webserverType The webserver type. * @return bool TRUE on success */ - public function checkPreConditions(CoreRelease $coreRelease) + public function checkPreConditions(CoreRelease $coreRelease, WebserverType $webserverType) { $success = true; // Folder structure test: Update can be done only if folder structure returns no errors - $folderStructureFacade = GeneralUtility::makeInstance(DefaultFactory::class)->getStructure(); + $folderStructureFacade = GeneralUtility::makeInstance(DefaultFactory::class)->getStructure($webserverType); $folderStructureMessageQueue = $folderStructureFacade->getStatus(); $folderStructureErrors = $folderStructureMessageQueue->getAllMessages(ContextualFeedbackSeverity::ERROR); $folderStructureWarnings = $folderStructureMessageQueue->getAllMessages(ContextualFeedbackSeverity::WARNING); diff --git a/typo3/sysext/install/Classes/WebserverType.php b/typo3/sysext/install/Classes/WebserverType.php new file mode 100644 index 0000000000000000000000000000000000000000..58946d69da3b819122ba4781631e7a95e7e0c94f --- /dev/null +++ b/typo3/sysext/install/Classes/WebserverType.php @@ -0,0 +1,69 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Install; + +use Psr\Http\Message\ServerRequestInterface; + +/** + * @internal This enum is only meant to be used within EXT:install and is not part of the TYPO3 Core API. + */ +enum WebserverType: string +{ + case Apache = 'apache'; + case MicrosoftInternetInformationServer = 'iis'; + case Other = 'other'; + + public static function fromRequest(ServerRequestInterface $request): self + { + return self::fromType((string)($request->getServerParams()['SERVER_SOFTWARE'] ?? '')); + } + + public static function fromType(string $type): self + { + if ($type === 'apache' || str_starts_with($type, 'Apache')) { + return self::Apache; + } + if ($type === 'iis' || str_starts_with($type, 'Microsoft-IIS')) { + return self::MicrosoftInternetInformationServer; + } + + return self::Other; + } + + /** + * @return array<string, non-empty-string> + */ + public static function getDescriptions(): array + { + return [ + self::Apache->value => 'Apache', + self::MicrosoftInternetInformationServer->value => 'Microsoft IIS', + self::Other->value => 'Other (use for anything else)', + ]; + } + + public function isApacheServer(): bool + { + return $this === self::Apache; + } + + public function isMicrosoftInternetInformationServer(): bool + { + return $this === self::MicrosoftInternetInformationServer; + } +} diff --git a/typo3/sysext/install/Tests/Unit/FolderStructure/DefaultFactoryTest.php b/typo3/sysext/install/Tests/Unit/FolderStructure/DefaultFactoryTest.php index f5fb42734e3f1a3486baf5cae966891e8d03248b..92ecad0b40bdafd28f1ba70616986d8551ae0693 100644 --- a/typo3/sysext/install/Tests/Unit/FolderStructure/DefaultFactoryTest.php +++ b/typo3/sysext/install/Tests/Unit/FolderStructure/DefaultFactoryTest.php @@ -19,6 +19,7 @@ namespace TYPO3\CMS\Install\Tests\Unit\FolderStructure; use TYPO3\CMS\Install\FolderStructure\DefaultFactory; use TYPO3\CMS\Install\FolderStructure\StructureFacadeInterface; +use TYPO3\CMS\Install\WebserverType; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; final class DefaultFactoryTest extends UnitTestCase @@ -28,7 +29,8 @@ final class DefaultFactoryTest extends UnitTestCase */ public function getStructureReturnsInstanceOfStructureFacadeInterface(): void { + $webserverType = WebserverType::fromType('i-dont-care'); $object = new DefaultFactory(); - self::assertInstanceOf(StructureFacadeInterface::class, $object->getStructure()); + self::assertInstanceOf(StructureFacadeInterface::class, $object->getStructure($webserverType)); } }