Spaces:
No application file
No application file
| declare(strict_types=1); | |
| namespace Mautic\InstallBundle\Command; | |
| use Doctrine\Persistence\ManagerRegistry; | |
| use Mautic\InstallBundle\Configurator\Step\CheckStep; | |
| use Mautic\InstallBundle\Configurator\Step\DoctrineStep; | |
| use Mautic\InstallBundle\Install\InstallService; | |
| use Symfony\Component\Console\Command\Command; | |
| use Symfony\Component\Console\Helper\QuestionHelper; | |
| use Symfony\Component\Console\Input\InputArgument; | |
| use Symfony\Component\Console\Input\InputInterface; | |
| use Symfony\Component\Console\Input\InputOption; | |
| use Symfony\Component\Console\Output\OutputInterface; | |
| use Symfony\Component\Console\Question\ConfirmationQuestion; | |
| /** | |
| * CLI Command to install Mautic. | |
| */ | |
| class InstallCommand extends Command | |
| { | |
| public const COMMAND = 'mautic:install'; | |
| public function __construct( | |
| private InstallService $installer, | |
| private ManagerRegistry $doctrineRegistry | |
| ) { | |
| parent::__construct(); | |
| } | |
| /** | |
| * Note: in every option (addOption()), please leave the default value empty to prevent problems with values from local.php being overwritten. | |
| */ | |
| protected function configure() | |
| { | |
| $this | |
| ->setName(self::COMMAND) | |
| ->setHelp('This command allows you to trigger the install process. It will try to get configuration values both from the local config file and command line options/arguments, where the latter takes precedence.') | |
| ->addArgument( | |
| 'site_url', | |
| InputArgument::REQUIRED, | |
| 'Site URL.', | |
| null | |
| ) | |
| ->addArgument( | |
| 'step', | |
| InputArgument::OPTIONAL, | |
| 'Install process start index. 0 for requirements check, 1 for database, 2 for admin, 3 for configuration, 4 for final step. Each successful step will trigger the next until completion.', | |
| 0 | |
| ) | |
| ->addOption( | |
| '--force', | |
| '-f', | |
| InputOption::VALUE_NONE, | |
| 'Do not ask confirmation if recommendations triggered.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_driver', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database driver.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_host', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database host.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_port', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database port.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_name', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database name.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_user', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database user.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_password', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database password.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_table_prefix', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database tables prefix.', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_backup_tables', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Backup database tables if they exist; otherwise drop them. (true|false)', | |
| null | |
| ) | |
| ->addOption( | |
| '--db_backup_prefix', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Database backup tables prefix.', | |
| null | |
| ) | |
| ->addOption( | |
| '--admin_firstname', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Admin first name.', | |
| null | |
| ) | |
| ->addOption( | |
| '--admin_lastname', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Admin last name.', | |
| null | |
| ) | |
| ->addOption( | |
| '--admin_username', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Admin username.', | |
| null | |
| ) | |
| ->addOption( | |
| '--admin_email', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Admin email.', | |
| null | |
| ) | |
| ->addOption( | |
| '--admin_password', | |
| null, | |
| InputOption::VALUE_REQUIRED, | |
| 'Admin user.', | |
| null | |
| ); | |
| parent::configure(); | |
| } | |
| protected function execute(InputInterface $input, OutputInterface $output): int | |
| { | |
| // Check Mautic is not already installed | |
| if ($this->installer->checkIfInstalled()) { | |
| $output->writeln('Mautic already installed'); | |
| return Command::SUCCESS; | |
| } | |
| $output->writeln([ | |
| 'Mautic Install', | |
| '==============', | |
| '', | |
| ]); | |
| if (!defined('IS_PHPUNIT')) { | |
| // Prevents querying of database tables that do not exist during the installation process | |
| define('MAUTIC_INSTALLER', 1); | |
| } | |
| // Build objects to pass to the install service from local.php or command line options | |
| $output->writeln('Parsing options and arguments...'); | |
| $options = $input->getOptions(); | |
| // Convert boolean options to actual booleans. | |
| $options['db_backup_tables'] = (bool) filter_var($options['db_backup_tables'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); | |
| /** | |
| * We need to have some default database parameters, as it could be the case that the | |
| * user didn't set them both in local.php and the command line options. | |
| */ | |
| $dbParams = [ | |
| 'driver' => 'pdo_mysql', | |
| 'host' => null, | |
| 'port' => null, | |
| 'name' => null, | |
| 'user' => null, | |
| 'password' => null, | |
| 'table_prefix' => null, | |
| 'backup_tables' => true, | |
| 'backup_prefix' => 'bak_', | |
| ]; | |
| $adminParam = [ | |
| 'firstname' => 'Admin', | |
| 'lastname' => 'Mautic', | |
| 'username' => 'admin', | |
| ]; | |
| $allParams = $this->installer->localConfigParameters(); | |
| // Initialize DB and admin params from local.php | |
| foreach ((array) $allParams as $opt => $value) { | |
| if (str_starts_with($opt, 'db_')) { | |
| $dbParams[substr($opt, 3)] = $value; | |
| } elseif (str_starts_with($opt, 'admin_')) { | |
| $adminParam[substr($opt, 6)] = $value; | |
| } | |
| } | |
| // Initialize DB and admin params from cli options | |
| foreach ($options as $opt => $value) { | |
| if (isset($value)) { | |
| if (str_starts_with($opt, 'db_')) { | |
| $dbParams[substr($opt, 3)] = $value; | |
| $allParams[$opt] = $value; | |
| } elseif (str_starts_with($opt, 'admin_')) { | |
| $adminParam[substr($opt, 6)] = $value; | |
| } | |
| } | |
| } | |
| if (!empty($allParams['site_url'])) { | |
| $siteUrl = $allParams['site_url']; | |
| } else { | |
| $siteUrl = $input->getArgument('site_url'); | |
| $allParams['site_url'] = $siteUrl; | |
| } | |
| $step = (float) $input->getArgument('step'); | |
| switch ($step) { | |
| default: | |
| case InstallService::CHECK_STEP: | |
| $output->writeln($step.' - Checking installation requirements...'); | |
| $messages = $this->stepAction($this->installer, ['site_url' => $siteUrl], $step); | |
| if (!empty($messages)) { | |
| if (isset($messages['requirements']) && !empty($messages['requirements'])) { | |
| // Stop install if requirements not met | |
| $output->writeln('Missing requirements:'); | |
| $this->handleInstallerErrors($output, $messages['requirements']); | |
| $output->writeln('Install canceled'); | |
| return (int) -$step; | |
| } elseif (isset($messages['optional']) && !empty($messages['optional'])) { | |
| $output->writeln('Missing optional settings:'); | |
| $this->handleInstallerErrors($output, $messages['optional']); | |
| if (empty($options['force'])) { | |
| // Ask user to confirm install when optional settings missing | |
| /** @var QuestionHelper $helper */ | |
| $helper = $this->getHelper('question'); | |
| $question = new ConfirmationQuestion('Continue with install anyway? [yes/no]', false); | |
| if (!$helper->ask($input, $output, $question)) { | |
| return (int) -$step; | |
| } | |
| } | |
| } | |
| } | |
| $output->writeln('Ready to Install!'); | |
| // Keep on with next step | |
| $step = InstallService::DOCTRINE_STEP; | |
| // no break | |
| case InstallService::DOCTRINE_STEP: | |
| $output->writeln($step.' - Creating database...'); | |
| /** | |
| * This is needed for installations with database prefixes to work correctly. | |
| */ | |
| $connectionWrapper = $this->doctrineRegistry->getConnection(); | |
| $connectionWrapper->initConnection($dbParams); | |
| $messages = $this->stepAction($this->installer, $dbParams, $step); | |
| if (!empty($messages)) { | |
| $output->writeln('Errors in database configuration/installation:'); | |
| $this->handleInstallerErrors($output, $messages); | |
| $output->writeln('Install canceled'); | |
| return (int) -$step; | |
| } | |
| $step = InstallService::DOCTRINE_STEP + .1; | |
| $output->writeln($step.' - Creating schema...'); | |
| $messages = $this->stepAction($this->installer, $dbParams, $step); | |
| if (!empty($messages)) { | |
| $output->writeln('Errors in schema configuration/installation:'); | |
| $this->handleInstallerErrors($output, $messages); | |
| $output->writeln('Install canceled'); | |
| return -InstallService::DOCTRINE_STEP; | |
| } | |
| $step = InstallService::DOCTRINE_STEP + .2; | |
| $output->writeln($step.' - Loading fixtures...'); | |
| $messages = $this->stepAction($this->installer, $dbParams, $step); | |
| if (!empty($messages)) { | |
| $output->writeln('Errors in fixtures configuration/installation:'); | |
| $this->handleInstallerErrors($output, $messages); | |
| $output->writeln('Install canceled'); | |
| return -InstallService::DOCTRINE_STEP; | |
| } | |
| // Keep on with next step | |
| $step = InstallService::USER_STEP; | |
| // no break | |
| case InstallService::USER_STEP: | |
| $output->writeln($step.' - Creating admin user...'); | |
| $messages = $this->stepAction($this->installer, $adminParam, $step); | |
| if (!empty($messages)) { | |
| $output->writeln('Errors in admin user configuration/installation:'); | |
| $this->handleInstallerErrors($output, $messages); | |
| $output->writeln('Install canceled'); | |
| return (int) -$step; | |
| } | |
| // Keep on with next step | |
| $step = InstallService::FINAL_STEP; | |
| // no break | |
| case InstallService::FINAL_STEP: | |
| $output->writeln($step.' - Final steps...'); | |
| $messages = $this->stepAction($this->installer, $allParams, $step); | |
| if (!empty($messages)) { | |
| $output->writeln('Errors in final step:'); | |
| $this->handleInstallerErrors($output, $messages); | |
| $output->writeln('Install canceled'); | |
| return (int) -$step; | |
| } | |
| } | |
| $output->writeln([ | |
| '', | |
| '================', | |
| 'Install complete', | |
| '================', | |
| ]); | |
| return Command::SUCCESS; | |
| } | |
| /** | |
| * Controller action for install steps. | |
| * | |
| * @param InstallService $installer The install process | |
| * @param array $params The install parameters | |
| * @param float $index The step number to process | |
| * | |
| * @throws \Exception | |
| */ | |
| protected function stepAction(InstallService $installer, array $params, float $index = 0): array | |
| { | |
| if ($index - floor($index) > 0) { | |
| $subIndex = (int) (round($index - floor($index), 1) * 10); | |
| $index = floor($index); | |
| } | |
| $index = (int) $index; | |
| $messages = []; | |
| switch ($index) { | |
| case InstallService::CHECK_STEP: | |
| // Check installation requirements | |
| $step = $installer->getStep($index); | |
| if ($step instanceof CheckStep) { | |
| // Set all step fields based on parameters | |
| $step->site_url = $params['site_url']; | |
| } | |
| $messages['requirements'] = $installer->checkRequirements($step); | |
| $messages['optional'] = $installer->checkOptionalSettings($step); | |
| break; | |
| case InstallService::DOCTRINE_STEP: | |
| $step = $installer->getStep($index); | |
| if ($step instanceof DoctrineStep) { | |
| // Set all step fields based on parameters | |
| foreach ($step as $key => $value) { | |
| if (isset($params[$key])) { | |
| $step->$key = $params[$key]; | |
| } | |
| } | |
| } | |
| if (!isset($subIndex)) { | |
| // Install database | |
| $messages = $installer->createDatabaseStep($step, $params); | |
| break; | |
| } | |
| switch ($subIndex) { | |
| case 1: | |
| // Install schema | |
| $messages = $installer->createSchemaStep($params); | |
| break; | |
| case 2: | |
| // Install fixtures | |
| $messages = $installer->createFixturesStep(); | |
| break; | |
| } | |
| break; | |
| case InstallService::USER_STEP: | |
| // Create admin user | |
| $messages = $installer->createAdminUserStep($params); | |
| break; | |
| case InstallService::FINAL_STEP: | |
| // Save final configuration | |
| $siteUrl = $params['site_url']; | |
| $messages = $installer->createFinalConfigStep($siteUrl); | |
| if (empty($messages)) { | |
| $installer->finalMigrationStep(); | |
| } | |
| break; | |
| } | |
| return $messages; | |
| } | |
| /** | |
| * Handle install command errors. | |
| * | |
| * @param array<string,string> $messages | |
| */ | |
| private function handleInstallerErrors(OutputInterface $output, array $messages): void | |
| { | |
| foreach ($messages as $type => $message) { | |
| $output->writeln(" - [$type] $message"); | |
| } | |
| } | |
| protected static $defaultDescription = 'Installs Mautic'; | |
| } | |