Failed Conditions
Pull Request — 4.0 (#4439)
by
unknown
05:39
created

InstallerCommand::execute()   B

Complexity

Conditions 6
Paths 36

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 36
nop 2
dl 0
loc 42
rs 8.6257
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Command;
15
16
use Doctrine\DBAL\DriverManager;
17
use Dotenv\Dotenv;
18
use Eccube\Util\StringUtil;
19
use Symfony\Component\Console\Command\Command;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Console\Question\ConfirmationQuestion;
23
use Symfony\Component\Console\Style\SymfonyStyle;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use Symfony\Component\Process\Exception\ProcessFailedException;
26
use Symfony\Component\Process\Process;
27
28
class InstallerCommand extends Command
29
{
30
    protected static $defaultName = 'eccube:install';
31
32
    /**
33
     * @var ContainerInterface
34
     */
35
    protected $container;
36
37
    /**
38
     * @var SymfonyStyle
39
     */
40
    protected $io;
41
42
    /**
43
     * @var string
44
     */
45
    protected $databaseUrl;
46
47
    public function __construct(ContainerInterface $container)
48
    {
49
        parent::__construct();
50
51
        $this->container = $container;
52
    }
53
54
    protected function configure()
55
    {
56
        $this
57
            ->setDescription('Install EC-CUBE');
58
    }
59
60
    protected function interact(InputInterface $input, OutputInterface $output)
61
    {
62
        $this->io->title('EC-CUBE Installer Interactive Wizard');
63
        $this->io->text([
64
            'If you prefer to not use this interactive wizard, define the environment valiables as follows:',
65
            '',
66
            ' $ export APP_ENV=dev',
67
            ' $ export APP_DEBUG=1',
68
            ' $ export DATABASE_URL=database_url',
69
            ' $ export DATABASE_SERVER_VERSION=server_version',
70
            ' $ export MAILER_URL=mailer_url',
71
            ' $ export ECCUBE_AUTH_MAGIC=auth_magic',
72
            ' ... and more',
73
            ' $ php bin/console eccube:install --no-interaction',
74
            '',
75
        ]);
76
77
        // DATABASE_URL
78
        $databaseUrl = $this->container->getParameter('eccube_database_url');
79
        if (empty($databaseUrl)) {
80
            $databaseUrl = 'sqlite:///var/eccube.db';
81
        }
82
        $databaseUrl = $this->io->ask('Database Url', $databaseUrl);
83
84
        // DATABASE_SERVER_VERSION
85
        $serverVersion = $this->getDatabaseServerVersion($databaseUrl);
86
87
        // MAILER_URL
88
        $mailerUrl = $this->container->getParameter('eccube_mailer_url');
89
        if (empty($mailerUrl)) {
90
            $mailerUrl = 'null://localhost';
91
        }
92
        $mailerUrl = $this->io->ask('Mailer Url', $mailerUrl);
93
94
        // ECCUBE_AUTH_MAGIC
95
        $authMagic = $this->container->getParameter('eccube_auth_magic');
96
        if (empty($authMagic) || $authMagic === '<change.me>') {
97
            $authMagic = StringUtil::random();
98
        }
99
        $authMagic = $this->io->ask('Auth Magic', $authMagic);
100
101
        // 以下環境変数に規定済の設定値があれば利用する
102
        // APP_ENV
103
        $appEnv = getenv('APP_ENV');
104
        // .envが存在しない状態では規定値'install'となっているため、
105
        // devに変更する。
106
        if (empty($appEnv) || $appEnv === 'install') {
107
            $appEnv = 'dev';
108
        }
109
110
        // APP_DEBUG
111
        $appDebug = getenv('APP_DEBUG');
112
        if (empty($appDebug)) {
113
            $appDebug = '1';
114
        }
115
116
        // ECCUBE_ADMIN_ROUTE
117
        $adminRoute = $this->container->getParameter('eccube_admin_route');
118
        if (empty($adminRoute)) {
119
            $adminRoute = 'admin';
120
        }
121
122
        // ECCUBE_TEMPLATE_CODE
123
        $templateCode = $this->container->getParameter('eccube_theme_code');
124
        if (empty($templateCode)) {
125
            $templateCode = 'default';
126
        }
127
128
        // ECCUBE_LOCALE
129
        $locale = $this->container->getParameter('locale');
130
        if (empty($locale)) {
131
            $locale = 'ja';
132
        }
133
134
        $this->io->caution('Execute the installation process. All data is initialized.');
135
        $question = new ConfirmationQuestion('Is it OK?');
136
        if (!$this->io->askQuestion($question)) {
137
            // `no`の場合はキャンセルメッセージを出力して終了する
138
            $this->setCode(function () {
139
                $this->io->success('EC-CUBE installation stopped.');
140
            });
141
142
            return;
143
        }
144
145
        $envParameters = [
146
            'APP_ENV' => $appEnv,
147
            'APP_DEBUG' => $appDebug,
148
            'DATABASE_URL' => $databaseUrl,
149
            'DATABASE_SERVER_VERSION' => $serverVersion,
150
            'MAILER_URL' => $mailerUrl,
151
            'ECCUBE_AUTH_MAGIC' => $authMagic,
152
            'ECCUBE_ADMIN_ROUTE' => $adminRoute,
153
            'ECCUBE_TEMPLATE_CODE' => $templateCode,
154
            'ECCUBE_LOCALE' => $locale,
155
        ];
156
157
        $envDir = $this->container->getParameter('kernel.project_dir');
158
        $envFile = $envDir.'/.env';
159
        $envDistFile = $envDir.'/.env.dist';
160
161
        $env = file_exists($envFile)
162
            ? file_get_contents($envFile)
163
            : file_get_contents($envDistFile);
164
165
        $env = StringUtil::replaceOrAddEnv($env, $envParameters);
166
167
        file_put_contents($envFile, $env);
168
    }
169
170
    protected function initialize(InputInterface $input, OutputInterface $output)
171
    {
172
        $this->io = new SymfonyStyle($input, $output);
173
    }
174
175
    protected function execute(InputInterface $input, OutputInterface $output)
176
    {
177
        // Process実行時に, APP_ENV/APP_DEBUGが子プロセスに引き継がれてしまうため,
178
        // 生成された.envをロードして上書きする.
179
        if ($input->isInteractive()) {
180
            $envDir = $this->container->getParameter('kernel.project_dir');
181
            if (file_exists($envDir.'/.env')) {
182
                (new Dotenv($envDir))->overload();
183
            }
184
        }
185
186
        // 対話モード実行時, container->getParameter('eccube_database_url')では
187
        // 更新後の値が取得できないため, getenv()を使用する.
188
        $databaseUrl = getenv('DATABASE_URL');
189
        $databaseName = $this->getDatabaseName($databaseUrl);
190
        $ifNotExists = $databaseName === 'sqlite' ? '' : ' --if-not-exists';
191
192
        // データベース作成, スキーマ作成, 初期データの投入を行う.
193
        $commands = [
194
            'doctrine:database:create'.$ifNotExists,
195
            'doctrine:schema:drop --force',
196
            'doctrine:schema:create',
197
            'eccube:fixtures:load',
198
            'cache:clear --no-warmup',
199
        ];
200
201
        // コンテナを再ロードするため別プロセスで実行する.
202
        foreach ($commands as $command) {
203
            try {
204
                $this->io->text(sprintf('<info>Run %s</info>...', $command));
205
                $process = new Process('bin/console '.$command);
206
                $process->mustRun();
207
                $this->io->text($process->getOutput());
208
            } catch (ProcessFailedException $e) {
209
                $this->io->error($e->getMessage());
210
211
                return;
212
            }
213
        }
214
215
        $this->io->success('EC-CUBE installation successful.');
216
    }
217
218
    protected function getDatabaseName($databaseUrl)
219
    {
220
        if (0 === strpos($databaseUrl, 'sqlite')) {
221
            return 'sqlite';
222
        }
223
        if (0 === strpos($databaseUrl, 'postgres')) {
224
            return 'postgres';
225
        }
226
        if (0 === strpos($databaseUrl, 'mysql')) {
227
            return 'mysql';
228
        }
229
230
        throw new \LogicException(sprintf('Database Url %s is invalid.', $databaseUrl));
231
    }
232
233
    protected function getDatabaseServerVersion($databaseUrl)
234
    {
235
        try {
236
            $conn = DriverManager::getConnection([
237
                'url' => $databaseUrl,
238
            ]);
239
        } catch (\Exception $e) {
240
            throw new \LogicException(sprintf('Database Url %s is invalid.', $databaseUrl));
241
        }
242
        $platform = $conn->getDatabasePlatform()->getName();
243 View Code Duplication
        switch ($platform) {
244
            case 'sqlite':
245
                $sql = 'SELECT sqlite_version() AS server_version';
246
                break;
247
            case 'mysql':
248
                $sql = 'SELECT version() AS server_version';
249
                break;
250
            case 'postgresql':
251
            default:
252
                $sql = 'SHOW server_version';
253
        }
254
        $stmt = $conn->executeQuery($sql);
255
        $version = $stmt->fetchColumn();
256
257
        if ($platform === 'postgresql') {
258
            preg_match('/\A([\d+\.]+)/', $version, $matches);
259
            $version = $matches[1];
260
        }
261
262
        return $version;
263
    }
264
}
265