diff --git a/bin/install b/bin/install
index c47036a9..b563f395 100755
--- a/bin/install
+++ b/bin/install
@@ -9,6 +9,7 @@ chdir(dirname(__DIR__));
require __DIR__ . '/../vendor/autoload.php';
$app = new Application();
-$app->add(new InstallCommand(new PhpArray()));
-$app->setDefaultCommand('shlink:install');
+$command = new InstallCommand(new PhpArray());
+$app->add($command);
+$app->setDefaultCommand($command->getName());
$app->run();
diff --git a/bin/update b/bin/update
index b226a9fa..86b7efc9 100755
--- a/bin/update
+++ b/bin/update
@@ -9,6 +9,7 @@ chdir(dirname(__DIR__));
require __DIR__ . '/../vendor/autoload.php';
$app = new Application();
-$app->add(new UpdateCommand(new PhpArray()));
-$app->setDefaultCommand('shlink:install');
+$command = new UpdateCommand(new PhpArray());
+$app->add($command);
+$app->setDefaultCommand($command->getName());
$app->run();
diff --git a/module/CLI/src/Command/Install/AbstractInstallCommand.php b/module/CLI/src/Command/Install/AbstractInstallCommand.php
new file mode 100644
index 00000000..472377d4
--- /dev/null
+++ b/module/CLI/src/Command/Install/AbstractInstallCommand.php
@@ -0,0 +1,323 @@
+ 'pdo_mysql',
+ 'PostgreSQL' => 'pdo_pgsql',
+ 'SQLite' => 'pdo_sqlite',
+ ];
+ const SUPPORTED_LANGUAGES = ['en', 'es'];
+
+ /**
+ * @var InputInterface
+ */
+ protected $input;
+ /**
+ * @var OutputInterface
+ */
+ protected $output;
+ /**
+ * @var QuestionHelper
+ */
+ private $questionHelper;
+ /**
+ * @var ProcessHelper
+ */
+ private $processHelper;
+ /**
+ * @var WriterInterface
+ */
+ private $configWriter;
+
+ /**
+ * InstallCommand constructor.
+ * @param WriterInterface $configWriter
+ */
+ public function __construct(WriterInterface $configWriter)
+ {
+ parent::__construct(null);
+ $this->configWriter = $configWriter;
+ }
+
+ public function configure()
+ {
+ $this->setName('shlink:install')
+ ->setDescription('Installs Shlink');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->input = $input;
+ $this->output = $output;
+ $this->questionHelper = $this->getHelper('question');
+ $this->processHelper = $this->getHelper('process');
+ $params = [];
+
+ $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 (file_exists('data/cache/app_config.php')) {
+ $output->write('Deleting old cached config...');
+ if (unlink('data/cache/app_config.php')) {
+ $output->writeln(' Success');
+ } else {
+ $output->writeln(
+ ' Failed! You will have to manually delete the data/cache/app_config.php file to get'
+ . ' new config applied.'
+ );
+ }
+ }
+
+ // Ask for custom config params
+ $params['DATABASE'] = $this->askDatabase();
+ $params['URL_SHORTENER'] = $this->askUrlShortener();
+ $params['LANGUAGE'] = $this->askLanguage();
+ $params['APP'] = $this->askApplication();
+
+ // Generate config params files
+ $config = $this->buildAppConfig($params);
+ $this->configWriter->toFile('config/params/generated_config.php', $config, false);
+ $output->writeln(['Custom configuration properly generated!', '']);
+
+ // Generate database
+ if (! $this->createDatabase()) {
+ 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;
+ }
+ }
+
+ protected function askDatabase()
+ {
+ $params = [];
+ $this->printTitle('DATABASE');
+
+ // Select database type
+ $databases = array_keys(self::DATABASE_DRIVERS);
+ $dbType = $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
+ 'Select database type (defaults to ' . $databases[0] . '):',
+ $databases,
+ 0
+ ));
+ $params['DRIVER'] = self::DATABASE_DRIVERS[$dbType];
+
+ // Ask for connection params if database is not SQLite
+ if ($params['DRIVER'] !== self::DATABASE_DRIVERS['SQLite']) {
+ $params['NAME'] = $this->ask('Database name', 'shlink');
+ $params['USER'] = $this->ask('Database username');
+ $params['PASSWORD'] = $this->ask('Database password');
+ $params['HOST'] = $this->ask('Database host', 'localhost');
+ $params['PORT'] = $this->ask('Database port', $this->getDefaultDbPort($params['DRIVER']));
+ }
+
+ return $params;
+ }
+
+ protected function getDefaultDbPort($driver)
+ {
+ return $driver === 'pdo_mysql' ? '3306' : '5432';
+ }
+
+ protected function askUrlShortener()
+ {
+ $this->printTitle('URL SHORTENER');
+
+ // Ask for URL shortener params
+ return [
+ 'SCHEMA' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
+ 'Select schema for generated short URLs (defaults to http):',
+ ['http', 'https'],
+ 0
+ )),
+ 'HOSTNAME' => $this->ask('Hostname for generated URLs'),
+ 'CHARS' => $this->ask(
+ 'Character set for generated short codes (leave empty to autogenerate one)',
+ null,
+ true
+ ) ?: str_shuffle(UrlShortener::DEFAULT_CHARS)
+ ];
+ }
+
+ protected function askLanguage()
+ {
+ $this->printTitle('LANGUAGE');
+
+ return [
+ 'DEFAULT' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
+ 'Select default language for the application in general (defaults to '
+ . self::SUPPORTED_LANGUAGES[0] . '):',
+ self::SUPPORTED_LANGUAGES,
+ 0
+ )),
+ 'CLI' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
+ 'Select default language for CLI executions (defaults to '
+ . self::SUPPORTED_LANGUAGES[0] . '):',
+ self::SUPPORTED_LANGUAGES,
+ 0
+ )),
+ ];
+ }
+
+ protected function askApplication()
+ {
+ $this->printTitle('APPLICATION');
+
+ return [
+ 'SECRET' => $this->ask(
+ 'Define a secret string that will be used to sign API tokens (leave empty to autogenerate one)',
+ null,
+ true
+ ) ?: $this->generateRandomString(32),
+ ];
+ }
+
+ /**
+ * @param string $text
+ */
+ protected function printTitle($text)
+ {
+ $text = trim($text);
+ $length = strlen($text) + 4;
+ $header = str_repeat('*', $length);
+
+ $this->output->writeln([
+ '',
+ '' . $header . '',
+ '* ' . strtoupper($text) . ' *',
+ '' . $header . '',
+ ]);
+ }
+
+ /**
+ * @param string $text
+ * @param string|null $default
+ * @param bool $allowEmpty
+ * @return string
+ * @throws RuntimeException
+ */
+ protected function ask($text, $default = null, $allowEmpty = false)
+ {
+ if (isset($default)) {
+ $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 array $params
+ * @return array
+ */
+ protected function buildAppConfig(array $params)
+ {
+ // Build simple config
+ $config = [
+ 'app_options' => [
+ 'secret_key' => $params['APP']['SECRET'],
+ ],
+ 'entity_manager' => [
+ 'connection' => [
+ 'driver' => $params['DATABASE']['DRIVER'],
+ ],
+ ],
+ 'translator' => [
+ 'locale' => $params['LANGUAGE']['DEFAULT'],
+ ],
+ 'cli' => [
+ 'locale' => $params['LANGUAGE']['CLI'],
+ ],
+ 'url_shortener' => [
+ 'domain' => [
+ 'schema' => $params['URL_SHORTENER']['SCHEMA'],
+ 'hostname' => $params['URL_SHORTENER']['HOSTNAME'],
+ ],
+ 'shortcode_chars' => $params['URL_SHORTENER']['CHARS'],
+ ],
+ ];
+
+ // Build dynamic database config
+ if ($params['DATABASE']['DRIVER'] === 'pdo_sqlite') {
+ $config['entity_manager']['connection']['path'] = 'data/database.sqlite';
+ } else {
+ $config['entity_manager']['connection']['user'] = $params['DATABASE']['USER'];
+ $config['entity_manager']['connection']['password'] = $params['DATABASE']['PASSWORD'];
+ $config['entity_manager']['connection']['dbname'] = $params['DATABASE']['NAME'];
+ $config['entity_manager']['connection']['host'] = $params['DATABASE']['HOST'];
+ $config['entity_manager']['connection']['port'] = $params['DATABASE']['PORT'];
+
+ if ($params['DATABASE']['DRIVER'] === 'pdo_mysql') {
+ $config['entity_manager']['connection']['driverOptions'] = [
+ \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
+ ];
+ }
+ }
+
+ return $config;
+ }
+
+ /**
+ * @return bool
+ */
+ abstract protected function createDatabase();
+
+ /**
+ * @param string $command
+ * @param string $errorMessage
+ * @return bool
+ */
+ protected 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;
+ }
+}
diff --git a/module/CLI/src/Command/Install/InstallCommand.php b/module/CLI/src/Command/Install/InstallCommand.php
index b331511a..a8e07de1 100644
--- a/module/CLI/src/Command/Install/InstallCommand.php
+++ b/module/CLI/src/Command/Install/InstallCommand.php
@@ -1,322 +1,11 @@
'pdo_mysql',
- 'PostgreSQL' => 'pdo_pgsql',
- 'SQLite' => 'pdo_sqlite',
- ];
- const SUPPORTED_LANGUAGES = ['en', 'es'];
-
- /**
- * @var InputInterface
- */
- private $input;
- /**
- * @var OutputInterface
- */
- private $output;
- /**
- * @var QuestionHelper
- */
- private $questionHelper;
- /**
- * @var ProcessHelper
- */
- private $processHelper;
- /**
- * @var WriterInterface
- */
- private $configWriter;
-
- /**
- * InstallCommand constructor.
- * @param WriterInterface $configWriter
- * @param callable|null $databaseCreationLogic
- */
- public function __construct(WriterInterface $configWriter)
- {
- parent::__construct(null);
- $this->configWriter = $configWriter;
- }
-
- public function configure()
- {
- $this->setName('shlink:install')
- ->setDescription('Installs Shlink');
- }
-
- public function execute(InputInterface $input, OutputInterface $output)
- {
- $this->input = $input;
- $this->output = $output;
- $this->questionHelper = $this->getHelper('question');
- $this->processHelper = $this->getHelper('process');
- $params = [];
-
- $output->writeln([
- 'Welcome to Shlink!!',
- 'This process will guide you through the installation.',
- ]);
-
- // Check if a cached config file exists and drop it if so
- if (file_exists('data/cache/app_config.php')) {
- $output->write('Deleting old cached config...');
- if (unlink('data/cache/app_config.php')) {
- $output->writeln(' Success');
- } else {
- $output->writeln(
- ' Failed! You will have to manually delete the data/cache/app_config.php file to get'
- . ' new config applied.'
- );
- }
- }
-
- // Ask for custom config params
- $params['DATABASE'] = $this->askDatabase();
- $params['URL_SHORTENER'] = $this->askUrlShortener();
- $params['LANGUAGE'] = $this->askLanguage();
- $params['APP'] = $this->askApplication();
-
- // Generate config params files
- $config = $this->buildAppConfig($params);
- $this->configWriter->toFile('config/params/generated_config.php', $config, false);
- $output->writeln(['Custom configuration properly generated!', '']);
-
- // Generate database
- if (! $this->createDatabase()) {
- 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;
- }
- }
-
- protected function askDatabase()
- {
- $params = [];
- $this->printTitle('DATABASE');
-
- // Select database type
- $databases = array_keys(self::DATABASE_DRIVERS);
- $dbType = $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
- 'Select database type (defaults to ' . $databases[0] . '):',
- $databases,
- 0
- ));
- $params['DRIVER'] = self::DATABASE_DRIVERS[$dbType];
-
- // Ask for connection params if database is not SQLite
- if ($params['DRIVER'] !== self::DATABASE_DRIVERS['SQLite']) {
- $params['NAME'] = $this->ask('Database name', 'shlink');
- $params['USER'] = $this->ask('Database username');
- $params['PASSWORD'] = $this->ask('Database password');
- $params['HOST'] = $this->ask('Database host', 'localhost');
- $params['PORT'] = $this->ask('Database port', $this->getDefaultDbPort($params['DRIVER']));
- }
-
- return $params;
- }
-
- protected function getDefaultDbPort($driver)
- {
- return $driver === 'pdo_mysql' ? '3306' : '5432';
- }
-
- protected function askUrlShortener()
- {
- $this->printTitle('URL SHORTENER');
-
- // Ask for URL shortener params
- return [
- 'SCHEMA' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
- 'Select schema for generated short URLs (defaults to http):',
- ['http', 'https'],
- 0
- )),
- 'HOSTNAME' => $this->ask('Hostname for generated URLs'),
- 'CHARS' => $this->ask(
- 'Character set for generated short codes (leave empty to autogenerate one)',
- null,
- true
- ) ?: str_shuffle(UrlShortener::DEFAULT_CHARS)
- ];
- }
-
- protected function askLanguage()
- {
- $this->printTitle('LANGUAGE');
-
- return [
- 'DEFAULT' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
- 'Select default language for the application in general (defaults to '
- . self::SUPPORTED_LANGUAGES[0] . '):',
- self::SUPPORTED_LANGUAGES,
- 0
- )),
- 'CLI' => $this->questionHelper->ask($this->input, $this->output, new ChoiceQuestion(
- 'Select default language for CLI executions (defaults to '
- . self::SUPPORTED_LANGUAGES[0] . '):',
- self::SUPPORTED_LANGUAGES,
- 0
- )),
- ];
- }
-
- protected function askApplication()
- {
- $this->printTitle('APPLICATION');
-
- return [
- 'SECRET' => $this->ask(
- 'Define a secret string that will be used to sign API tokens (leave empty to autogenerate one)',
- null,
- true
- ) ?: $this->generateRandomString(32),
- ];
- }
-
- /**
- * @param string $text
- */
- protected function printTitle($text)
- {
- $text = trim($text);
- $length = strlen($text) + 4;
- $header = str_repeat('*', $length);
-
- $this->output->writeln([
- '',
- '' . $header . '',
- '* ' . strtoupper($text) . ' *',
- '' . $header . '',
- ]);
- }
-
- /**
- * @param string $text
- * @param string|null $default
- * @param bool $allowEmpty
- * @return string
- */
- protected function ask($text, $default = null, $allowEmpty = false)
- {
- if (isset($default)) {
- $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) && empty($default) && ! $allowEmpty);
-
- return $value;
- }
-
- /**
- * @param array $params
- * @return array
- */
- protected function buildAppConfig(array $params)
- {
- // Build simple config
- $config = [
- 'app_options' => [
- 'secret_key' => $params['APP']['SECRET'],
- ],
- 'entity_manager' => [
- 'connection' => [
- 'driver' => $params['DATABASE']['DRIVER'],
- ],
- ],
- 'translator' => [
- 'locale' => $params['LANGUAGE']['DEFAULT'],
- ],
- 'cli' => [
- 'locale' => $params['LANGUAGE']['CLI'],
- ],
- 'url_shortener' => [
- 'domain' => [
- 'schema' => $params['URL_SHORTENER']['SCHEMA'],
- 'hostname' => $params['URL_SHORTENER']['HOSTNAME'],
- ],
- 'shortcode_chars' => $params['URL_SHORTENER']['CHARS'],
- ],
- ];
-
- // Build dynamic database config
- if ($params['DATABASE']['DRIVER'] === 'pdo_sqlite') {
- $config['entity_manager']['connection']['path'] = 'data/database.sqlite';
- } else {
- $config['entity_manager']['connection']['user'] = $params['DATABASE']['USER'];
- $config['entity_manager']['connection']['password'] = $params['DATABASE']['PASSWORD'];
- $config['entity_manager']['connection']['dbname'] = $params['DATABASE']['NAME'];
- $config['entity_manager']['connection']['host'] = $params['DATABASE']['HOST'];
- $config['entity_manager']['connection']['port'] = $params['DATABASE']['PORT'];
-
- if ($params['DATABASE']['DRIVER'] === 'pdo_mysql') {
- $config['entity_manager']['connection']['driverOptions'] = [
- \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
- ];
- }
- }
-
- return $config;
- }
-
protected function createDatabase()
{
$this->output->writeln('Initializing database...');
return $this->runCommand('php vendor/bin/doctrine.php orm:schema-tool:create', 'Error generating database.');
}
-
- /**
- * @param string $command
- * @param string $errorMessage
- * @return bool
- */
- protected function runCommand($command, $errorMessage)
- {
- $process = $this->processHelper->run($this->output, $command);
- if ($process->isSuccessful()) {
- $this->output->writeln(' Success!');
- return true;
- } else {
- if ($this->output->isVerbose()) {
- return false;
- }
- $this->output->writeln(
- ' ' . $errorMessage . ' Run this command with -vvv to see specific error info.'
- );
- return false;
- }
- }
}
diff --git a/module/CLI/src/Command/Install/UpdateCommand.php b/module/CLI/src/Command/Install/UpdateCommand.php
index 3aed5512..def28d06 100644
--- a/module/CLI/src/Command/Install/UpdateCommand.php
+++ b/module/CLI/src/Command/Install/UpdateCommand.php
@@ -1,9 +1,7 @@