Completed
Pull Request — master (#105)
by Mikołaj
04:26 queued 01:11
created

InstallCommand::execute()   C

Complexity

Conditions 10
Paths 60

Size

Total Lines 72
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 10.1371

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 43
c 2
b 0
f 0
nc 60
nop 2
dl 0
loc 72
ccs 32
cts 36
cp 0.8889
crap 10.1371
rs 5.9036

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Shlinkio\Shlink\CLI\Command\Install;
3
4
use Shlinkio\Shlink\CLI\Install\ConfigCustomizerPluginManagerInterface;
5
use Shlinkio\Shlink\CLI\Install\Plugin;
6
use Shlinkio\Shlink\CLI\Model\CustomizableAppConfig;
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Exception\LogicException;
9
use Symfony\Component\Console\Exception\RuntimeException;
10
use Symfony\Component\Console\Helper\ProcessHelper;
11
use Symfony\Component\Console\Helper\QuestionHelper;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use Symfony\Component\Console\Question\ConfirmationQuestion;
15
use Symfony\Component\Console\Question\Question;
16
use Symfony\Component\Filesystem\Exception\IOException;
17
use Symfony\Component\Filesystem\Filesystem;
18
use Zend\Config\Writer\WriterInterface;
19
20
class InstallCommand extends Command
21
{
22
    const GENERATED_CONFIG_PATH = 'config/params/generated_config.php';
23
24
    /**
25
     * @var InputInterface
26
     */
27
    private $input;
28
    /**
29
     * @var OutputInterface
30
     */
31
    private $output;
32
    /**
33
     * @var QuestionHelper
34
     */
35
    private $questionHelper;
36
    /**
37
     * @var ProcessHelper
38
     */
39
    private $processHelper;
40
    /**
41
     * @var WriterInterface
42
     */
43
    private $configWriter;
44
    /**
45
     * @var Filesystem
46
     */
47
    private $filesystem;
48
    /**
49
     * @var ConfigCustomizerPluginManagerInterface
50
     */
51
    private $configCustomizers;
52
    /**
53
     * @var bool
54
     */
55
    private $isUpdate;
56
57
    /**
58
     * InstallCommand constructor.
59
     * @param WriterInterface $configWriter
60
     * @param Filesystem $filesystem
61
     * @param bool $isUpdate
62
     * @throws LogicException
63
     */
64 5
    public function __construct(
65
        WriterInterface $configWriter,
66
        Filesystem $filesystem,
67
        ConfigCustomizerPluginManagerInterface $configCustomizers,
68
        $isUpdate = false
69
    ) {
70 5
        parent::__construct();
71 5
        $this->configWriter = $configWriter;
72 5
        $this->isUpdate = $isUpdate;
73 5
        $this->filesystem = $filesystem;
74 5
        $this->configCustomizers = $configCustomizers;
75 5
    }
76
77 5
    public function configure()
78
    {
79
        $this
80 5
            ->setName('shlink:install')
81 5
            ->setDescription('Installs or updates Shlink');
82 5
    }
83
84 4
    public function execute(InputInterface $input, OutputInterface $output)
85
    {
86 4
        $this->input = $input;
87 4
        $this->output = $output;
88 4
        $this->questionHelper = $this->getHelper('question');
89 4
        $this->processHelper = $this->getHelper('process');
90
91 4
        $output->writeln([
92 4
            '<info>Welcome to Shlink!!</info>',
93
            'This will guide you through the installation process.',
94
        ]);
95
96
        // Check if a cached config file exists and drop it if so
97 4
        if ($this->filesystem->exists('data/cache/app_config.php')) {
98 2
            $output->write('Deleting old cached config...');
99
            try {
100 2
                $this->filesystem->remove('data/cache/app_config.php');
101 1
                $output->writeln(' <info>Success</info>');
102 1
            } catch (IOException $e) {
103 1
                $output->writeln(
104
                    ' <error>Failed!</error> You will have to manually delete the data/cache/app_config.php file to get'
105 1
                    . ' new config applied.'
106
                );
107 1
                if ($output->isVerbose()) {
108
                    $this->getApplication()->renderException($e, $output);
109
                }
110 1
                return;
111
            }
112
        }
113
114
        // If running update command, ask the user to import previous config
115 3
        $config = $this->isUpdate ? $this->importConfig() : new CustomizableAppConfig();
116
117
        // Ask for custom config params
118
        foreach ([
119 3
            Plugin\DatabaseConfigCustomizerPlugin::class,
120
            Plugin\UrlShortenerConfigCustomizerPlugin::class,
121
            Plugin\LanguageConfigCustomizerPlugin::class,
122
            Plugin\ApplicationConfigCustomizerPlugin::class,
123
        ] as $pluginName) {
124
            /** @var Plugin\ConfigCustomizerPluginInterface $configCustomizer */
125 3
            $configCustomizer = $this->configCustomizers->get($pluginName);
126 3
            $configCustomizer->process($input, $output, $config);
127
        }
128
129
        // Generate config params files
130 3
        $this->configWriter->toFile(self::GENERATED_CONFIG_PATH, $config->getArrayCopy(), false);
131 3
        $output->writeln(['<info>Custom configuration properly generated!</info>', '']);
132
133
        // If current command is not update, generate database
134 3
        if (!  $this->isUpdate) {
135 2
            $this->output->writeln('Initializing database...');
136 2
            if (! $this->runCommand(
137 2
                'php vendor/bin/doctrine.php orm:schema-tool:create',
138 2
                'Error generating database.'
139
            )) {
140
                return;
141
            }
142
        }
143
144
        // Run database migrations
145 3
        $output->writeln('Updating database...');
146 3
        if (! $this->runCommand('php vendor/bin/doctrine-migrations migrations:migrate', 'Error updating database.')) {
147
            return;
148
        }
149
150
        // Generate proxies
151 3
        $output->writeln('Generating proxies...');
152 3
        if (! $this->runCommand('php vendor/bin/doctrine.php orm:generate-proxies', 'Error generating proxies.')) {
153
            return;
154
        }
155 3
    }
156
157
    /**
158
     * @return CustomizableAppConfig
159
     * @throws RuntimeException
160
     */
161 1
    private function importConfig()
162
    {
163 1
        $config = new CustomizableAppConfig();
164
165
        // Ask the user if he/she wants to import an older configuration
166 1
        $importConfig = $this->questionHelper->ask($this->input, $this->output, new ConfirmationQuestion(
167 1
            '<question>Do you want to import previous configuration? (Y/n):</question> '
168
        ));
169 1
        if (! $importConfig) {
170
            return $config;
171
        }
172
173
        // Ask the user for the older shlink path
174 1
        $keepAsking = true;
175
        do {
176 1
            $config->setImportedInstallationPath($this->ask(
177 1
                'Previous shlink installation path from which to import config'
178
            ));
179 1
            $configFile = $config->getImportedInstallationPath() . '/' . self::GENERATED_CONFIG_PATH;
180 1
            $configExists = $this->filesystem->exists($configFile);
181
182 1
            if (! $configExists) {
183 1
                $keepAsking = $this->questionHelper->ask($this->input, $this->output, new ConfirmationQuestion(
184
                    'Provided path does not seem to be a valid shlink root path. '
185 1
                    . '<question>Do you want to try another path? (Y/n):</question> '
186
                ));
187
            }
188 1
        } while (! $configExists && $keepAsking);
189
190
        // If after some retries the user has chosen not to test another path, return
191 1
        if (! $configExists) {
192
            return $config;
193
        }
194
195
        // Read the config file
196 1
        $config->exchangeArray(include $configFile);
197 1
        return $config;
198
    }
199
200
    /**
201
     * @param string $text
202
     * @param string|null $default
203
     * @param bool $allowEmpty
204
     * @return string
205
     * @throws RuntimeException
206
     */
207 1
    private function ask($text, $default = null, $allowEmpty = false)
208
    {
209 1
        if ($default !== null) {
210
            $text .= ' (defaults to ' . $default . ')';
211
        }
212
        do {
213 1
            $value = $this->questionHelper->ask($this->input, $this->output, new Question(
214 1
                '<question>' . $text . ':</question> ',
215 1
                $default
216
            ));
217 1
            if (empty($value) && ! $allowEmpty) {
218
                $this->output->writeln('<error>Value can\'t be empty</error>');
219
            }
220 1
        } while (empty($value) && $default === null && ! $allowEmpty);
221
222 1
        return $value;
223
    }
224
225
    /**
226
     * @param string $command
227
     * @param string $errorMessage
228
     * @return bool
229
     */
230 3
    private function runCommand($command, $errorMessage)
231
    {
232 3
        $process = $this->processHelper->run($this->output, $command);
233 3
        if ($process->isSuccessful()) {
234 3
            $this->output->writeln('    <info>Success!</info>');
235 3
            return true;
236
        }
237
238
        if ($this->output->isVerbose()) {
239
            return false;
240
        }
241
242
        $this->output->writeln(
243
            '    <error>' . $errorMessage . '</error>  Run this command with -vvv to see specific error info.'
244
        );
245
        return false;
246
    }
247
}
248