Completed
Pull Request — 4.0 (#4439)
by Hideki
07:52 queued 03:15
created

InstallerCommand.php$0 ➔ updateEnvFile()   A

Complexity

Conditions 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 14
rs 9.7998
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
    private $envFileUpdater;
48
49
    public function __construct(ContainerInterface $container)
50
    {
51
        parent::__construct();
52
53
        $this->container = $container;
54
55
        /* env更新処理無名クラス */
56
        $this->envFileUpdater = new class() {
57
            public $appEnv;
58
            public $appDebug;
59
            public $databaseUrl;
60
            public $serverVersion;
61
            public $mailerUrl;
62
            public $authMagic;
63
            public $adminRoute;
64
            public $templateCode;
65
            public $locale;
66
67
            public $envDir;
68
69
            private function getEnvParameters()
70
            {
71
                return  [
72
                            'APP_ENV' => $this->appEnv,
73
                            'APP_DEBUG' => $this->appDebug,
74
                            'DATABASE_URL' => $this->databaseUrl,
75
                            'DATABASE_SERVER_VERSION' => $this->serverVersion,
76
                            'MAILER_URL' => $this->mailerUrl,
77
                            'ECCUBE_AUTH_MAGIC' => $this->authMagic,
78
                            'ECCUBE_ADMIN_ROUTE' => $this->adminRoute,
79
                            'ECCUBE_TEMPLATE_CODE' => $this->templateCode,
80
                            'ECCUBE_LOCALE' => $this->locale,
81
                        ];
82
            }
83
84
            /**
85
             * envファイル更新処理
86
             */
87
            public function updateEnvFile()
88
            {
89
                // $envDir = $this->container->getParameter('kernel.project_dir');
90
                $envFile = $this->envDir.'/.env';
91
                $envDistFile = $this->envDir.'/.env.dist';
92
93
                $env = file_exists($envFile)
94
                            ? file_get_contents($envFile)
95
                            : file_get_contents($envDistFile);
96
97
                $env = StringUtil::replaceOrAddEnv($env, $this->getEnvParameters());
98
99
                file_put_contents($envFile, $env);
100
            }
101
        };
102
    }
103
104
    protected function configure()
105
    {
106
        $this
107
            ->setDescription('Install EC-CUBE');
108
    }
109
110
    protected function interact(InputInterface $input, OutputInterface $output)
111
    {
112
        $this->io->title('EC-CUBE Installer Interactive Wizard');
113
        $this->io->text([
114
            'If you prefer to not use this interactive wizard, define the environment valiables as follows:',
115
            '',
116
            ' $ export APP_ENV=dev',
117
            ' $ export APP_DEBUG=1',
118
            ' $ export DATABASE_URL=database_url',
119
            ' $ export DATABASE_SERVER_VERSION=server_version',
120
            ' $ export MAILER_URL=mailer_url',
121
            ' $ export ECCUBE_AUTH_MAGIC=auth_magic',
122
            ' ... and more',
123
            ' $ php bin/console eccube:install --no-interaction',
124
            '',
125
        ]);
126
127
        // DATABASE_URL
128
        $databaseUrl = $this->container->getParameter('eccube_database_url');
129
        if (empty($databaseUrl)) {
130
            $databaseUrl = 'sqlite:///var/eccube.db';
131
        }
132
        $this->envFileUpdater->databaseUrl = $this->io->ask('Database Url', $databaseUrl);
133
134
        // DATABASE_SERVER_VERSION
135
        $this->envFileUpdater->serverVersion = $this->getDatabaseServerVersion($databaseUrl);
136
137
        // MAILER_URL
138
        $mailerUrl = $this->container->getParameter('eccube_mailer_url');
139
        if (empty($mailerUrl)) {
140
            $mailerUrl = 'null://localhost';
141
        }
142
        $this->envFileUpdater->mailerUrl = $this->io->ask('Mailer Url', $mailerUrl);
143
144
        // ECCUBE_AUTH_MAGIC
145
        $authMagic = $this->container->getParameter('eccube_auth_magic');
146
        if (empty($authMagic) || $authMagic === '<change.me>') {
147
            $authMagic = StringUtil::random();
148
        }
149
        $this->envFileUpdater->authMagic = $this->io->ask('Auth Magic', $authMagic);
150
151
        // 以下環境変数に規定済の設定値があれば利用する
152
        // APP_ENV
153
        $appEnv = env('APP_ENV', 'dev');
154
        // .envが存在しない状態では規定値'install'となっているため、devに更新する
155
        if ($appEnv === 'install') {
156
            $appEnv = 'dev';
157
        }
158
        $this->envFileUpdater->appEnv = $appEnv;
159
160
        // APP_DEBUG
161
        $this->envFileUpdater->appDebug = env('APP_DEBUG', '1');
162
163
        // ECCUBE_ADMIN_ROUTE
164
        $adminRoute = $this->container->getParameter('eccube_admin_route');
165
        if (empty($adminRoute)) {
166
            $adminRoute = 'admin';
167
        }
168
        $this->envFileUpdater->adminRoute = $adminRoute;
169
170
        // ECCUBE_TEMPLATE_CODE
171
        $templateCode = $this->container->getParameter('eccube_theme_code');
172
        if (empty($templateCode)) {
173
            $templateCode = 'default';
174
        }
175
        $this->envFileUpdater->templateCode = $templateCode;
176
177
        // ECCUBE_LOCALE
178
        $locale = $this->container->getParameter('locale');
179
        if (empty($locale)) {
180
            $locale = 'ja';
181
        }
182
        $this->envFileUpdater->locale = $locale;
183
184
        $this->io->caution('Execute the installation process. All data is initialized.');
185
        $question = new ConfirmationQuestion('Is it OK?');
186
        if (!$this->io->askQuestion($question)) {
187
            // `no`の場合はキャンセルメッセージを出力して終了する
188
            $this->setCode(function () {
189
                $this->io->success('EC-CUBE installation stopped.');
190
            });
191
192
            return;
193
        }
194
195
        // envファイルへの更新反映処理
196
        $this->envFileUpdater->envDir = $this->container->getParameter('kernel.project_dir');
197
        $this->envFileUpdater->updateEnvFile();
198
    }
199
200
    protected function initialize(InputInterface $input, OutputInterface $output)
201
    {
202
        $this->io = new SymfonyStyle($input, $output);
203
    }
204
205
    protected function execute(InputInterface $input, OutputInterface $output)
206
    {
207
        // Process実行時に, APP_ENV/APP_DEBUGが子プロセスに引き継がれてしまうため,
208
        // 生成された.envをロードして上書きする.
209
        if ($input->isInteractive()) {
210
            $envDir = $this->container->getParameter('kernel.project_dir');
211
            if (file_exists($envDir.'/.env')) {
212
                (new Dotenv($envDir))->overload();
213
            }
214
        }
215
216
        // 対話モード実行時, container->getParameter('eccube_database_url')では
217
        // 更新後の値が取得できないため, getenv()を使用する.
218
        $databaseUrl = getenv('DATABASE_URL');
219
        $databaseName = $this->getDatabaseName($databaseUrl);
220
        $ifNotExists = $databaseName === 'sqlite' ? '' : ' --if-not-exists';
221
222
        // データベース作成, スキーマ作成, 初期データの投入を行う.
223
        $commands = [
224
            'doctrine:database:create'.$ifNotExists,
225
            'doctrine:schema:drop --force',
226
            'doctrine:schema:create',
227
            'eccube:fixtures:load',
228
            'cache:clear --no-warmup',
229
        ];
230
231
        // コンテナを再ロードするため別プロセスで実行する.
232
        foreach ($commands as $command) {
233
            try {
234
                $this->io->text(sprintf('<info>Run %s</info>...', $command));
235
                $process = new Process('bin/console '.$command);
236
                $process->mustRun();
237
                $this->io->text($process->getOutput());
238
            } catch (ProcessFailedException $e) {
239
                $this->io->error($e->getMessage());
240
241
                return;
242
            }
243
        }
244
245
        $this->io->success('EC-CUBE installation successful.');
246
    }
247
248
    protected function getDatabaseName($databaseUrl)
249
    {
250
        if (0 === strpos($databaseUrl, 'sqlite')) {
251
            return 'sqlite';
252
        }
253
        if (0 === strpos($databaseUrl, 'postgres')) {
254
            return 'postgres';
255
        }
256
        if (0 === strpos($databaseUrl, 'mysql')) {
257
            return 'mysql';
258
        }
259
260
        throw new \LogicException(sprintf('Database Url %s is invalid.', $databaseUrl));
261
    }
262
263
    protected function getDatabaseServerVersion($databaseUrl)
264
    {
265
        try {
266
            $conn = DriverManager::getConnection([
267
                'url' => $databaseUrl,
268
            ]);
269
        } catch (\Exception $e) {
270
            throw new \LogicException(sprintf('Database Url %s is invalid.', $databaseUrl));
271
        }
272
        $platform = $conn->getDatabasePlatform()->getName();
273 View Code Duplication
        switch ($platform) {
274
            case 'sqlite':
275
                $sql = 'SELECT sqlite_version() AS server_version';
276
                break;
277
            case 'mysql':
278
                $sql = 'SELECT version() AS server_version';
279
                break;
280
            case 'postgresql':
281
            default:
282
                $sql = 'SHOW server_version';
283
        }
284
        $stmt = $conn->executeQuery($sql);
285
        $version = $stmt->fetchColumn();
286
287
        if ($platform === 'postgresql') {
288
            preg_match('/\A([\d+\.]+)/', $version, $matches);
289
            $version = $matches[1];
290
        }
291
292
        return $version;
293
    }
294
}
295