configWriter = $configWriter; $this->isUpdate = $isUpdate; $this->filesystem = $filesystem; $this->configCustomizers = $configCustomizers; } public function configure() { $this ->setName('shlink:install') ->setDescription('Installs or updates Shlink'); } public function execute(InputInterface $input, OutputInterface $output) { $this->input = $input; $this->output = $output; $this->questionHelper = $this->getHelper('question'); $this->processHelper = $this->getHelper('process'); $output->writeln([ 'Welcome to Shlink!!', 'This will guide you through the installation process.', ]); // Check if a cached config file exists and drop it if so if ($this->filesystem->exists('data/cache/app_config.php')) { $output->write('Deleting old cached config...'); try { $this->filesystem->remove('data/cache/app_config.php'); $output->writeln(' Success'); } catch (IOException $e) { $output->writeln( ' Failed! You will have to manually delete the data/cache/app_config.php file to get' . ' new config applied.' ); if ($output->isVerbose()) { $this->getApplication()->renderException($e, $output); } return; } } // If running update command, ask the user to import previous config $config = $this->isUpdate ? $this->importConfig() : new CustomizableAppConfig(); // Ask for custom config params foreach ([ Plugin\DatabaseConfigCustomizerPlugin::class, Plugin\UrlShortenerConfigCustomizerPlugin::class, Plugin\LanguageConfigCustomizerPlugin::class, Plugin\ApplicationConfigCustomizerPlugin::class, ] as $pluginName) { /** @var Plugin\ConfigCustomizerPluginInterface $configCustomizer */ $configCustomizer = $this->configCustomizers->get($pluginName); $configCustomizer->process($input, $output, $config); } // Generate config params files $this->configWriter->toFile(self::GENERATED_CONFIG_PATH, $config->getArrayCopy(), false); $output->writeln(['Custom configuration properly generated!', '']); // If current command is not update, generate database if (! $this->isUpdate) { $this->output->writeln('Initializing database...'); if (! $this->runCommand( 'php vendor/bin/doctrine.php orm:schema-tool:create', 'Error generating database.' )) { return; } } // Run database migrations $output->writeln('Updating database...'); if (! $this->runCommand('php vendor/bin/doctrine-migrations migrations:migrate', 'Error updating database.')) { return; } // Generate proxies $output->writeln('Generating proxies...'); if (! $this->runCommand('php vendor/bin/doctrine.php orm:generate-proxies', 'Error generating proxies.')) { return; } } /** * @return CustomizableAppConfig * @throws RuntimeException */ private function importConfig() { $config = new CustomizableAppConfig(); // Ask the user if he/she wants to import an older configuration $importConfig = $this->questionHelper->ask($this->input, $this->output, new ConfirmationQuestion( 'Do you want to import previous configuration? (Y/n): ' )); if (! $importConfig) { return $config; } // Ask the user for the older shlink path $keepAsking = true; do { $config->setImportedInstallationPath($this->ask( 'Previous shlink installation path from which to import config' )); $configFile = $config->getImportedInstallationPath() . '/' . self::GENERATED_CONFIG_PATH; $configExists = $this->filesystem->exists($configFile); if (! $configExists) { $keepAsking = $this->questionHelper->ask($this->input, $this->output, new ConfirmationQuestion( 'Provided path does not seem to be a valid shlink root path. ' . 'Do you want to try another path? (Y/n): ' )); } } while (! $configExists && $keepAsking); // If after some retries the user has chosen not to test another path, return if (! $configExists) { return $config; } // Read the config file $config->exchangeArray(include $configFile); return $config; } /** * @param string $text * @param string|null $default * @param bool $allowEmpty * @return string * @throws RuntimeException */ private function ask($text, $default = null, $allowEmpty = false) { if ($default !== null) { $text .= ' (defaults to ' . $default . ')'; } do { $value = $this->questionHelper->ask($this->input, $this->output, new Question( '' . $text . ': ', $default )); if (empty($value) && ! $allowEmpty) { $this->output->writeln('Value can\'t be empty'); } } while (empty($value) && $default === null && ! $allowEmpty); return $value; } /** * @param string $command * @param string $errorMessage * @return bool */ private function runCommand($command, $errorMessage) { $process = $this->processHelper->run($this->output, $command); if ($process->isSuccessful()) { $this->output->writeln(' Success!'); return true; } if ($this->output->isVerbose()) { return false; } $this->output->writeln( ' ' . $errorMessage . ' Run this command with -vvv to see specific error info.' ); return false; } }