Completed
Push — 4.0 ( 268f2c...88f012 )
by Hideki
05:48 queued 10s
created

Eccube/Controller/Install/InstallController.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Controller\Install;
15
16
use Doctrine\Common\Annotations\AnnotationReader;
17
use Doctrine\Common\Annotations\CachedReader;
18
use Doctrine\Common\Cache\ArrayCache;
19
use Doctrine\DBAL\Connection;
20
use Doctrine\DBAL\DriverManager;
21
use Doctrine\DBAL\Migrations\Configuration\Configuration;
22
use Doctrine\DBAL\Migrations\Migration;
23
use Doctrine\DBAL\Migrations\MigrationException;
24
use Doctrine\DBAL\Types\Type;
25
use Doctrine\ORM\EntityManager;
26
use Doctrine\ORM\Tools\SchemaTool;
27
use Doctrine\ORM\Tools\Setup;
28
use Eccube\Common\Constant;
29
use Eccube\Controller\AbstractController;
30
use Eccube\Doctrine\DBAL\Types\UTCDateTimeType;
31
use Eccube\Doctrine\DBAL\Types\UTCDateTimeTzType;
32
use Eccube\Doctrine\ORM\Mapping\Driver\AnnotationDriver;
33
use Eccube\Form\Type\Install\Step1Type;
34
use Eccube\Form\Type\Install\Step3Type;
35
use Eccube\Form\Type\Install\Step4Type;
36
use Eccube\Form\Type\Install\Step5Type;
37
use Eccube\Security\Core\Encoder\PasswordEncoder;
38
use Eccube\Util\CacheUtil;
39
use Eccube\Util\StringUtil;
40
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
41
use Symfony\Component\Filesystem\Filesystem;
42
use Symfony\Component\Finder\Finder;
43
use Symfony\Component\HttpFoundation\Request;
44
use Symfony\Component\HttpFoundation\Session\SessionInterface;
45
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
46
use Symfony\Component\Routing\Annotation\Route;
47
48
class InstallController extends AbstractController
49
{
50
    /**
51
     * default value of auth magic
52
     */
53
    const DEFAULT_AUTH_MAGIC = '<change.me>';
54
55
    protected $requiredModules = [
56
        'pdo',
57
        'phar',
58
        'mbstring',
59
        'zlib',
60
        'ctype',
61
        'session',
62
        'JSON',
63
        'xml',
64
        'libxml',
65
        'OpenSSL',
66
        'zip',
67
        'cURL',
68
        'fileinfo',
69
        'intl',
70
    ];
71
72
    protected $recommendedModules = [
73
        'hash',
74
        'mcrypt',
75
    ];
76
77
    protected $eccubeDirs = [
78
        'app/Plugin',
79
        'app/PluginData',
80
        'app/proxy',
81
        'app/template',
82
        'html',
83
        'var',
84
        'vendor',
85
    ];
86
87
    protected $eccubeFiles = [
88
        'composer.json',
89
        'composer.lock',
90
    ];
91
92
    /**
93
     * @var PasswordEncoder
94
     */
95
    protected $encoder;
96
97
    /**
98
     * @var CacheUtil
99
     */
100
    protected $cacheUtil;
101
102
    public function __construct(PasswordEncoder $encoder, CacheUtil $cacheUtil)
103
    {
104
        $this->encoder = $encoder;
105
        $this->cacheUtil = $cacheUtil;
106
    }
107
108
    /**
109
     * 最初からやり直す場合、SESSION情報をクリア.
110
     *
111
     * @Route("/", name="homepage")
112
     * @Route("/install", name="install")
113
     *
114
     * @Template("index.twig")
115
     *
116
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
117
     */
118
    public function index()
119
    {
120
        if (!$this->isInstallEnv()) {
121
            throw new NotFoundHttpException();
122
        }
123
124
        $this->removeSessionData($this->session);
125
126
        return $this->redirectToRoute('install_step1');
127
    }
128
129
    /**
130
     * ようこそ.
131
     *
132
     * @Route("/install/step1", name="install_step1")
133
     * @Template("step1.twig")
134
     *
135
     * @param Request $request
136
     *
137
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
138
     */
139
    public function step1(Request $request)
140
    {
141
        if (!$this->isInstallEnv()) {
142
            throw new NotFoundHttpException();
143
        }
144
145
        $form = $this->formFactory
146
            ->createBuilder(Step1Type::class)
147
            ->getForm();
148
149
        $form->setData($this->getSessionData($this->session));
150
        $form->handleRequest($request);
151
152 View Code Duplication
        if ($form->isSubmitted() && $form->isValid()) {
153
            $this->setSessionData($this->session, $form->getData());
154
155
            return $this->redirectToRoute('install_step2');
156
        }
157
158
        $this->checkModules();
159
160
        $authmagic = $this->getParameter('eccube_auth_magic');
161
        if ($authmagic === self::DEFAULT_AUTH_MAGIC) {
162
            $authmagic = StringUtil::random(32);
163
        }
164
        $this->setSessionData($this->session, ['authmagic' => $authmagic]);
165
166
        return [
167
            'form' => $form->createView(),
168
        ];
169
    }
170
171
    /**
172
     * ディレクトリとファイルの書き込み権限をチェック.
173
     *
174
     * @Route("/install/step2", name="install_step2")
175
     * @Template("step2.twig")
176
     *
177
     * @return array
178
     */
179
    public function step2()
180
    {
181
        if (!$this->isInstallEnv()) {
182
            throw new NotFoundHttpException();
183
        }
184
185
        $noWritePermissions = [];
186
187
        $projectDir = $this->getParameter('kernel.project_dir');
188
189
        $eccubeDirs = array_map(function ($dir) use ($projectDir) {
190
            return $projectDir.'/'.$dir;
191
        }, $this->eccubeDirs);
192
193
        $eccubeFiles = array_map(function ($file) use ($projectDir) {
194
            return $projectDir.'/'.$file;
195
        }, $this->eccubeFiles);
196
197
        // ルートディレクトリの書き込み権限をチェック
198
        if (!is_writable($projectDir)) {
199
            $noWritePermissions[] = $projectDir;
200
        }
201
202
        // 対象ディレクトリの書き込み権限をチェック
203
        foreach ($eccubeDirs as $dir) {
204
            if (!is_writable($dir)) {
205
                $noWritePermissions[] = $dir;
206
            }
207
        }
208
209
        // 対象ディレクトリ配下のディレクトリ・ファイルの書き込み権限をチェック
210
        $finder = Finder::create()->in($eccubeDirs);
211
        foreach ($finder as $file) {
212
            if (!is_writable($file->getRealPath())) {
213
                $noWritePermissions[] = $file;
214
            }
215
        }
216
217
        // composer.json, composer.lockの書き込み権限をチェック
218
        foreach ($eccubeFiles as $file) {
219
            if (!is_writable($file)) {
220
                $noWritePermissions[] = $file;
221
            }
222
        }
223
224
        $faviconPath = '/assets/img/common/favicon.ico';
225
        if (!file_exists($this->getParameter('eccube_html_dir').'/user_data'.$faviconPath)) {
226
            $file = new Filesystem();
227
            $file->copy(
228
                $this->getParameter('eccube_html_front_dir').$faviconPath,
229
                $this->getParameter('eccube_html_dir').'/user_data'.$faviconPath
230
            );
231
        }
232
233
        $logoPath = '/assets/pdf/logo.png';
234
        if (!file_exists($this->getParameter('eccube_html_dir').'/user_data'.$logoPath)) {
235
            $file = new Filesystem();
236
            $file->copy(
237
                $this->getParameter('eccube_html_admin_dir').$logoPath,
238
                $this->getParameter('eccube_html_dir').'/user_data'.$logoPath
239
            );
240
        }
241
242
        return [
243
            'noWritePermissions' => $noWritePermissions,
244
        ];
245
    }
246
247
    /**
248
     * サイトの設定.
249
     *
250
     * @Route("/install/step3", name="install_step3")
251
     * @Template("step3.twig")
252
     *
253
     * @param Request $request
254
     *
255
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
256
     *
257
     * @throws \Doctrine\DBAL\DBALException
258
     * @throws \Exception
259
     */
260
    public function step3(Request $request)
261
    {
262
        if (!$this->isInstallEnv()) {
263
            throw new NotFoundHttpException();
264
        }
265
266
        $sessionData = $this->getSessionData($this->session);
267
268
        // 再インストールの場合は環境変数から復旧
269
        if ($this->isInstalled()) {
270
            // ショップ名/メールアドレス
271
            $conn = $this->entityManager->getConnection();
272
            $stmt = $conn->query('SELECT shop_name, email01 FROM dtb_base_info WHERE id = 1;');
273
            $row = $stmt->fetch();
274
            $sessionData['shop_name'] = $row['shop_name'];
275
            $sessionData['email'] = $row['email01'];
276
277
            $databaseUrl = $this->getParameter('eccube_database_url');
278
            $sessionData = array_merge($sessionData, $this->extractDatabaseUrl($databaseUrl));
279
280
            // 管理画面ルーティング
281
            $sessionData['admin_dir'] = $this->getParameter('eccube_admin_route');
282
283
            // 管理画面許可IP
284
            $sessionData['admin_allow_hosts'] = implode($this->getParameter('eccube_admin_allow_hosts'));
285
286
            // 強制SSL
287
            $sessionData['admin_force_ssl'] = $this->getParameter('eccube_force_ssl');
288
289
            // メール
290
            $mailerUrl = $this->getParameter('eccube_mailer_url');
291
            $sessionData = array_merge($sessionData, $this->extractMailerUrl($mailerUrl));
292
        } else {
293
            // 初期値設定
294
            if (!isset($sessionData['admin_allow_hosts'])) {
295
                $sessionData['admin_allow_hosts'] = '';
296
            }
297
            if (!isset($sessionData['smtp_host'])) {
298
                $sessionData = array_merge($sessionData, $this->extractMailerUrl('smtp://localhost:25'));
299
            }
300
        }
301
302
        $form = $this->formFactory
303
            ->createBuilder(Step3Type::class)
304
            ->getForm();
305
306
        $form->setData($sessionData);
307
        $form->handleRequest($request);
308
309 View Code Duplication
        if ($form->isSubmitted() && $form->isValid()) {
310
            $this->setSessionData($this->session, $form->getData());
311
312
            return $this->redirectToRoute('install_step4');
313
        }
314
315
        return [
316
            'form' => $form->createView(),
317
            'request' => $request,
318
        ];
319
    }
320
321
    /**
322
     * データベースの設定.
323
     *
324
     * @Route("/install/step4", name="install_step4")
325
     * @Template("step4.twig")
326
     *
327
     * @param Request $request
328
     *
329
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
330
     *
331
     * @throws \Exception
332
     */
333
    public function step4(Request $request)
334
    {
335
        if (!$this->isInstallEnv()) {
336
            throw new NotFoundHttpException();
337
        }
338
339
        $sessionData = $this->getSessionData($this->session);
340
341
        if (empty($sessionData['database'])) {
342
            // 再インストールの場合は環境変数から復旧.
343
            if ($this->isInstalled()) {
344
                $databaseUrl = $this->getParameter('eccube_database_url');
345
                $sessionData = array_merge($sessionData, $this->extractDatabaseUrl($databaseUrl));
346
            }
347
        }
348
349
        $form = $this->formFactory
350
            ->createBuilder(Step4Type::class)
351
            ->getForm();
352
353
        $form->setData($sessionData);
354
        $form->handleRequest($request);
355
356
        if ($form->isSubmitted() && $form->isValid()) {
357
            $data = $form->getData();
358
            if ($data['database'] === 'pdo_sqlite') {
359
                $data['database_name'] = '/var/eccube.db';
360
            }
361
362
            $this->setSessionData($this->session, $data);
363
364
            return $this->redirectToRoute('install_step5');
365
        }
366
367
        return [
368
            'form' => $form->createView(),
369
        ];
370
    }
371
372
    /**
373
     * データベースの初期化.
374
     *
375
     * @Route("/install/step5", name="install_step5")
376
     * @Template("step5.twig")
377
     *
378
     * @param Request $request
379
     *
380
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
381
     *
382
     * @throws \Exception
383
     */
384
    public function step5(Request $request)
385
    {
386
        if (!$this->isInstallEnv()) {
387
            throw new NotFoundHttpException();
388
        }
389
390
        $form = $this->formFactory
391
            ->createBuilder(Step5Type::class)
392
            ->getForm();
393
394
        $sessionData = $this->getSessionData($this->session);
395
        $form->setData($sessionData);
396
        $form->handleRequest($request);
397
398
        if ($form->isSubmitted() && $form->isValid()) {
399
            $noUpdate = $form['no_update']->getData();
400
401
            $url = $this->createDatabaseUrl($sessionData);
402
403
            try {
404
                $conn = $this->createConnection(['url' => $url]);
405
                $em = $this->createEntityManager($conn);
406
                $migration = $this->createMigration($conn);
407
408
                if ($noUpdate) {
409
                    $this->update($conn, [
410
                        'auth_magic' => $sessionData['authmagic'],
411
                        'login_id' => $sessionData['login_id'],
412
                        'login_pass' => $sessionData['login_pass'],
413
                        'shop_name' => $sessionData['shop_name'],
414
                        'email' => $sessionData['email'],
415
                    ]);
416
                } else {
417
                    $this->dropTables($em);
418
                    $this->createTables($em);
419
                    $this->importCsv($em);
420
                    $this->migrate($migration);
421
                    $this->insert($conn, [
422
                        'auth_magic' => $sessionData['authmagic'],
423
                        'login_id' => $sessionData['login_id'],
424
                        'login_pass' => $sessionData['login_pass'],
425
                        'shop_name' => $sessionData['shop_name'],
426
                        'email' => $sessionData['email'],
427
                    ]);
428
                }
429
            } catch (\Exception $e) {
430
                log_error($e->getMessage());
431
                $this->addError($e->getMessage());
432
433
                return [
434
                    'form' => $form->createView(),
435
                ];
436
            }
437
438
            if (isset($sessionData['agree']) && $sessionData['agree']) {
439
                $host = $request->getSchemeAndHttpHost();
440
                $basePath = $request->getBasePath();
441
                $params = [
442
                    'http_url' => $host.$basePath,
443
                    'shop_name' => $sessionData['shop_name'],
444
                ];
445
                $this->sendAppData($params, $em);
446
            }
447
            $version = $this->getDatabaseVersion($em);
448
            $this->setSessionData($this->session, ['database_version' => $version]);
449
450
            return $this->redirectToRoute('install_complete');
451
        }
452
453
        return [
454
            'form' => $form->createView(),
455
        ];
456
    }
457
458
    /**
459
     * インストール完了
460
     *
461
     * @Route("/install/complete", name="install_complete")
462
     * @Template("complete.twig")
463
     */
464
    public function complete(Request $request)
465
    {
466
        if (!$this->isInstallEnv()) {
467
            throw new NotFoundHttpException();
468
        }
469
470
        $sessionData = $this->getSessionData($this->session);
471
        $databaseUrl = $this->createDatabaseUrl($sessionData);
472
        $mailerUrl = $this->createMailerUrl($sessionData);
473
        $forceSSL = isset($sessionData['admin_force_ssl']) ? (bool) $sessionData['admin_force_ssl'] : false;
474 View Code Duplication
        if ($forceSSL === false) {
475
            $forceSSL = 'false';
476
        } elseif ($forceSSL === true) {
477
            $forceSSL = 'true';
478
        }
479
        $env = file_get_contents(__DIR__.'/../../../../.env.dist');
480
        $replacement = [
481
            'APP_ENV' => 'prod',
482
            'APP_DEBUG' => '0',
483
            'DATABASE_URL' => $databaseUrl,
484
            'MAILER_URL' => $mailerUrl,
485
            'ECCUBE_AUTH_MAGIC' => $sessionData['authmagic'],
486
            'DATABASE_SERVER_VERSION' => isset($sessionData['database_version']) ? $sessionData['database_version'] : '3',
487
            'ECCUBE_ADMIN_ALLOW_HOSTS' => $this->convertAdminAllowHosts($sessionData['admin_allow_hosts']),
488
            'ECCUBE_FORCE_SSL' => $forceSSL,
489
            'ECCUBE_ADMIN_ROUTE' => isset($sessionData['admin_dir']) ? $sessionData['admin_dir'] : 'admin',
490
            'ECCUBE_COOKIE_PATH' => $request->getBasePath() ? $request->getBasePath() : '/',
491
            'ECCUBE_TEMPLATE_CODE' => 'default',
492
            'ECCUBE_LOCALE' => 'ja',
493
        ];
494
495
        $env = StringUtil::replaceOrAddEnv($env, $replacement);
496
497
        if ($this->getParameter('kernel.environment') === 'install') {
498
            file_put_contents(__DIR__.'/../../../../.env', $env);
499
        }
500
        $host = $request->getSchemeAndHttpHost();
501
        $basePath = $request->getBasePath();
502
        $adminUrl = $host.$basePath.'/'.$replacement['ECCUBE_ADMIN_ROUTE'];
503
504
        $this->removeSessionData($this->session);
505
506
        $this->cacheUtil->clearCache('prod');
507
508
        return [
509
            'admin_url' => $adminUrl,
510
        ];
511
    }
512
513
    protected function getSessionData(SessionInterface $session)
514
    {
515
        return $session->get('eccube.session.install', []);
516
    }
517
518
    protected function removeSessionData(SessionInterface $session)
519
    {
520
        $session->clear();
521
    }
522
523
    protected function setSessionData(SessionInterface $session, $data = [])
524
    {
525
        $data = array_replace_recursive($this->getSessionData($session), $data);
526
        $session->set('eccube.session.install', $data);
527
    }
528
529
    protected function checkModules()
530
    {
531
        foreach ($this->requiredModules as $module) {
532
            if (!extension_loaded($module)) {
533
                $this->addDanger(trans('install.required_extension_disabled', ['%module%' => $module]), 'install');
534
            }
535
        }
536
        if (!extension_loaded('pdo_mysql') && !extension_loaded('pdo_pgsql')) {
537
            $this->addDanger(trans('install.required_database_extension_disabled'), 'install');
538
        }
539
        foreach ($this->recommendedModules as $module) {
540
            if (!extension_loaded($module)) {
541
                if ($module == 'mcrypt' && PHP_VERSION_ID >= 70100) {
542
                    //The mcrypt extension has been deprecated in PHP 7.1.x
543
                    //http://php.net/manual/en/migration71.deprecated.php
544
                    continue;
545
                }
546
                $this->addInfo(trans('install.recommend_extension_disabled', ['%module%' => $module]), 'install');
547
            }
548
        }
549
        if ('\\' === DIRECTORY_SEPARATOR) { // for Windows
550
            if (!extension_loaded('wincache')) {
551
                $this->addInfo(trans('install.recommend_extension_disabled', ['%module%' => 'wincache']), 'install');
552
            }
553
        } else {
554
            if (!extension_loaded('apc')) {
555
                $this->addInfo(trans('install.recommend_extension_disabled', ['%module%' => 'apc']), 'install');
556
            }
557
        }
558
        if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) {
559
            if (!function_exists('apache_get_modules')) {
560
                $this->addWarning(trans('install.mod_rewrite_unknown'), 'install');
561
            } elseif (!in_array('mod_rewrite', apache_get_modules())) {
562
                $this->addDanger(trans('install.mod_rewrite_disabled'), 'install');
563
            }
564
        } elseif (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false) {
565
            // iis
566
        } elseif (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
567
            // nginx
568
        }
569
    }
570
571
    protected function createConnection(array $params)
572
    {
573
        if (strpos($params['url'], 'mysql') !== false) {
574
            $params['charset'] = 'utf8';
575
            $params['defaultTableOptions'] = [
576
                'collate' => 'utf8_general_ci',
577
            ];
578
        }
579
580
        Type::overrideType('datetime', UTCDateTimeType::class);
581
        Type::overrideType('datetimetz', UTCDateTimeTzType::class);
582
583
        $conn = DriverManager::getConnection($params);
584
        $conn->ping();
585
586
        $platform = $conn->getDatabasePlatform();
587
        $platform->markDoctrineTypeCommented('datetime');
588
        $platform->markDoctrineTypeCommented('datetimetz');
589
590
        return $conn;
591
    }
592
593
    protected function createEntityManager(Connection $conn)
594
    {
595
        $paths = [
596
            $this->getParameter('kernel.project_dir').'/src/Eccube/Entity',
597
            $this->getParameter('kernel.project_dir').'/app/Customize/Entity',
598
        ];
599
        $config = Setup::createConfiguration(true);
600
        $driver = new AnnotationDriver(new CachedReader(new AnnotationReader(), new ArrayCache()), $paths);
601
        $driver->setTraitProxiesDirectory($this->getParameter('kernel.project_dir').'/app/proxy/entity');
602
        $config->setMetadataDriverImpl($driver);
603
604
        $em = EntityManager::create($conn, $config);
605
606
        return $em;
607
    }
608
609
    /**
610
     * @param array $params
611
     *
612
     * @return string
613
     */
614
    public function createDatabaseUrl(array $params)
615
    {
616
        if (!isset($params['database'])) {
617
            return null;
618
        }
619
620
        $url = '';
621
        switch ($params['database']) {
622
            case 'pdo_sqlite':
623
                $url = 'sqlite://'.$params['database_name'];
624
                break;
625
626
            case 'pdo_mysql':
627
            case 'pdo_pgsql':
628
                $url = str_replace('pdo_', '', $params['database']);
629
                $url .= '://';
630
                if (isset($params['database_user'])) {
631
                    $url .= $params['database_user'];
632
                    if (isset($params['database_password'])) {
633
                        $url .= ':'.\rawurlencode($params['database_password']);
634
                    }
635
                    $url .= '@';
636
                }
637 View Code Duplication
                if (isset($params['database_host'])) {
638
                    $url .= $params['database_host'];
639
                    if (isset($params['database_port'])) {
640
                        $url .= ':'.$params['database_port'];
641
                    }
642
                    $url .= '/';
643
                }
644
                $url .= $params['database_name'];
645
                break;
646
        }
647
648
        return $url;
649
    }
650
651
    /**
652
     * @param string $url
653
     *
654
     * @return array
655
     */
656
    public function extractDatabaseUrl($url)
657
    {
658
        if (preg_match('|^sqlite://(.*)$|', $url, $matches)) {
659
            return [
660
                'database' => 'pdo_sqlite',
661
                'database_name' => $matches[1],
662
            ];
663
        }
664
665
        $parsed = parse_url($url);
666
667
        if ($parsed === false) {
668
            throw new \Exception('Malformed parameter "url".');
669
        }
670
671
        return [
672
            'database' => 'pdo_'.$parsed['scheme'],
673
            'database_name' => ltrim($parsed['path'], '/'),
674
            'database_host' => $parsed['host'],
675
            'database_port' => isset($parsed['port']) ? $parsed['port'] : null,
676
            'database_user' => isset($parsed['user']) ? $parsed['user'] : null,
677
            'database_password' => isset($parsed['pass']) ? $parsed['pass'] : null,
678
        ];
679
    }
680
681
    /**
682
     * @param array $params
683
     *
684
     * @return string
685
     *
686
     * @see https://github.com/symfony/swiftmailer-bundle/blob/9728097df87e76e2db71fc41fd7d211c06daea3e/DependencyInjection/SwiftmailerTransportFactory.php#L80-L142
687
     */
688
    public function createMailerUrl(array $params)
689
    {
690
        $url = '';
691
        if (isset($params['transport'])) {
692
            $url = $params['transport'].'://';
693
        } else {
694
            $url = 'smtp://';
695
        }
696 View Code Duplication
        if (isset($params['smtp_username'])) {
697
            $url .= $params['smtp_username'];
698
            if (isset($params['smtp_password'])) {
699
                $url .= ':'.$params['smtp_password'];
700
            }
701
            $url .= '@';
702
        }
703
704
        $queryStrings = [];
705
        if (isset($params['encryption'])) {
706
            $queryStrings['encryption'] = $params['encryption'];
707
            if ($params['encryption'] === 'ssl' && !isset($params['smtp_port'])) {
708
                $params['smtp_port'] = 465;
709
            }
710
        }
711
        if (isset($params['auth_mode'])) {
712
            $queryStrings['auth_mode'] = $params['auth_mode'];
713
        } else {
714
            if (isset($params['smtp_username'])) {
715
                $queryStrings['auth_mode'] = 'plain';
716
            }
717
        }
718
        ksort($queryStrings, SORT_STRING);
719
720 View Code Duplication
        if (isset($params['smtp_host'])) {
721
            $url .= $params['smtp_host'];
722
            if (isset($params['smtp_port'])) {
723
                $url .= ':'.$params['smtp_port'];
724
            }
725
        }
726
727
        if (isset($params['smtp_username']) || array_values($queryStrings)) {
728
            $url .= '?';
729
            $i = count($queryStrings);
730
            foreach ($queryStrings as $key => $value) {
731
                $url .= $key.'='.$value;
732
                if ($i > 1) {
733
                    $url .= '&';
734
                }
735
                $i--;
736
            }
737
        }
738
739
        return $url;
740
    }
741
742
    /**
743
     * @param string $url
744
     *
745
     * @return array
746
     */
747
    public function extractMailerUrl($url)
748
    {
749
        $options = [
750
            'transport' => null,
751
            'smtp_username' => null,
752
            'smtp_password' => null,
753
            'smtp_host' => null,
754
            'smtp_port' => null,
755
            'encryption' => null,
756
            'auth_mode' => null,
757
        ];
758
759
        if ($url) {
760
            $parts = parse_url($url);
761
            if (isset($parts['scheme'])) {
762
                $options['transport'] = $parts['scheme'];
763
            }
764
            if (isset($parts['user'])) {
765
                $options['smtp_username'] = $parts['user'];
766
            }
767
            if (isset($parts['pass'])) {
768
                $options['smtp_password'] = $parts['pass'];
769
            }
770
            if (isset($parts['host'])) {
771
                $options['smtp_host'] = $parts['host'];
772
            }
773
            if (isset($parts['port'])) {
774
                $options['smtp_port'] = $parts['port'];
775
            }
776
            if (isset($parts['query'])) {
777
                parse_str($parts['query'], $query);
778
                foreach (array_keys($options) as $key) {
779
                    if (isset($query[$key]) && $query[$key] != '') {
780
                        $options[$key] = $query[$key];
781
                    }
782
                }
783
            }
784
        }
785
        if (!isset($options['transport'])) {
786
            $options['transport'] = 'smtp';
787
        } elseif ('gmail' === $options['transport']) {
788
            $options['encryption'] = 'ssl';
789
            $options['auth_mode'] = 'login';
790
            $options['smtp_host'] = 'smtp.gmail.com';
791
            $options['transport'] = 'smtp';
792
        }
793
        if (!isset($options['smtp_port'])) {
794
            $options['smtp_port'] = 'ssl' === $options['encryption'] ? 465 : 25;
795
        }
796
        if (isset($options['smtp_username']) && !isset($options['auth_mode'])) {
797
            $options['auth_mode'] = 'plain';
798
        }
799
        ksort($options, SORT_STRING);
800
801
        return $options;
802
    }
803
804
    protected function createMigration(Connection $conn)
805
    {
806
        $config = new Configuration($conn);
807
        $config->setMigrationsNamespace('DoctrineMigrations');
808
        $migrationDir = $this->getParameter('kernel.project_dir').'/src/Eccube/Resource/doctrine/migration';
809
        $config->setMigrationsDirectory($migrationDir);
810
        $config->registerMigrationsFromDirectory($migrationDir);
811
812
        $migration = new Migration($config);
813
        $migration->setNoMigrationException(true);
814
815
        return $migration;
816
    }
817
818
    protected function dropTables(EntityManager $em)
819
    {
820
        $metadatas = $em->getMetadataFactory()->getAllMetadata();
821
        $schemaTool = new SchemaTool($em);
822
        $schemaTool->dropSchema($metadatas);
823
        $em->getConnection()->executeQuery('DROP TABLE IF EXISTS doctrine_migration_versions');
824
    }
825
826
    protected function createTables(EntityManager $em)
827
    {
828
        $metadatas = $em->getMetadataFactory()->getAllMetadata();
829
        $schemaTool = new SchemaTool($em);
830
        $schemaTool->createSchema($metadatas);
831
    }
832
833
    protected function importCsv(EntityManager $em)
834
    {
835
        // for full locale code cases
836
        $locale = env('ECCUBE_LOCALE', 'ja_JP');
837
        $locale = str_replace('_', '-', $locale);
838
        $locales = \Locale::parseLocale($locale);
839
        $localeDir = is_null($locales) ? 'ja' : $locales['language'];
840
841
        $loader = new \Eccube\Doctrine\Common\CsvDataFixtures\Loader();
842
        $loader->loadFromDirectory($this->getParameter('kernel.project_dir').'/src/Eccube/Resource/doctrine/import_csv/'.$localeDir);
843
        $executer = new \Eccube\Doctrine\Common\CsvDataFixtures\Executor\DbalExecutor($em);
844
        $fixtures = $loader->getFixtures();
845
        $executer->execute($fixtures);
846
    }
847
848
    protected function insert(Connection $conn, array $data)
849
    {
850
        $conn->beginTransaction();
851
        try {
852
            $salt = StringUtil::random(32);
853
            $this->encoder->setAuthMagic($data['auth_magic']);
854
            $password = $this->encoder->encodePassword($data['login_pass'], $salt);
855
856
            $id = ('postgresql' === $conn->getDatabasePlatform()->getName())
857
                ? $conn->fetchColumn("select nextval('dtb_base_info_id_seq')")
858
                : null;
859
860
            $conn->insert('dtb_base_info', [
861
                'id' => $id,
862
                'shop_name' => $data['shop_name'],
863
                'email01' => $data['email'],
864
                'email02' => $data['email'],
865
                'email03' => $data['email'],
866
                'email04' => $data['email'],
867
                'update_date' => new \DateTime(),
868
                'discriminator_type' => 'baseinfo',
869
            ], [
870
                'update_date' => \Doctrine\DBAL\Types\Type::DATETIME,
871
            ]);
872
873
            $member_id = ('postgresql' === $conn->getDatabasePlatform()->getName())
874
                ? $conn->fetchColumn("select nextval('dtb_member_id_seq')")
875
                : null;
876
877
            $conn->insert('dtb_member', [
878
                'id' => $member_id,
879
                'login_id' => $data['login_id'],
880
                'password' => $password,
881
                'salt' => $salt,
882
                'work_id' => 1,
883
                'authority_id' => 0,
884
                'creator_id' => 1,
885
                'sort_no' => 1,
886
                'update_date' => new \DateTime(),
887
                'create_date' => new \DateTime(),
888
                'name' => trans('install.member_name'),
889
                'department' => $data['shop_name'],
890
                'discriminator_type' => 'member',
891
            ], [
892
                'update_date' => Type::DATETIME,
893
                'create_date' => Type::DATETIME,
894
            ]);
895
            $conn->commit();
896
        } catch (\Exception $e) {
897
            $conn->rollback();
898
            throw $e;
899
        }
900
    }
901
902
    protected function update(Connection $conn, array $data)
903
    {
904
        $conn->beginTransaction();
905
        try {
906
            $salt = StringUtil::random(32);
907
            $stmt = $conn->prepare('SELECT id FROM dtb_member WHERE login_id = :login_id;');
908
            $stmt->execute([':login_id' => $data['login_id']]);
909
            $row = $stmt->fetch();
910
            $this->encoder->setAuthMagic($data['auth_magic']);
911
            $password = $this->encoder->encodePassword($data['login_pass'], $salt);
912
            if ($row) {
913
                // 同一の管理者IDであればパスワードのみ更新
914
                $sth = $conn->prepare('UPDATE dtb_member set password = :password, salt = :salt, update_date = current_timestamp WHERE login_id = :login_id;');
915
                $sth->execute([
916
                    ':password' => $password,
917
                    ':salt' => $salt,
918
                    ':login_id' => $data['login_id'],
919
                ]);
920
            } else {
921
                // 新しい管理者IDが入力されたらinsert
922
                $sth = $conn->prepare("INSERT INTO dtb_member (login_id, password, salt, work_id, authority_id, creator_id, sort_no, update_date, create_date,name,department,discriminator_type) VALUES (:login_id, :password , :salt , '1', '0', '1', '1', current_timestamp, current_timestamp,'管理者','EC-CUBE SHOP', 'member');");
923
                $sth->execute([
924
                    ':login_id' => $data['login_id'],
925
                    ':password' => $password,
926
                    ':salt' => $salt,
927
                ]);
928
            }
929
            $stmt = $conn->prepare('UPDATE dtb_base_info set
930
                shop_name = :shop_name,
931
                email01 = :admin_mail,
932
                email02 = :admin_mail,
933
                email03 = :admin_mail,
934
                email04 = :admin_mail,
935
                update_date = current_timestamp
936
            WHERE id = 1;');
937
            $stmt->execute([
938
                ':shop_name' => $data['shop_name'],
939
                ':admin_mail' => $data['email'],
940
            ]);
941
            $conn->commit();
942
        } catch (\Exception $e) {
943
            $conn->rollback();
944
            throw $e;
945
        }
946
    }
947
948
    public function migrate(Migration $migration)
949
    {
950
        try {
951
            // nullを渡すと最新バージョンまでマイグレートする
952
            $migration->migrate(null, false);
953
        } catch (MigrationException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
954
        }
955
    }
956
957
    /**
958
     * @param array $params
959
     * @param EntityManager $em
960
     *
961
     * @return array
962
     */
963
    public function createAppData($params, EntityManager $em)
964
    {
965
        $platform = $em->getConnection()->getDatabasePlatform()->getName();
966
        $version = $this->getDatabaseVersion($em);
967
        $data = [
968
            'site_url' => $params['http_url'],
969
            'shop_name' => $params['shop_name'],
970
            'cube_ver' => Constant::VERSION,
971
            'php_ver' => phpversion(),
972
            'db_ver' => $platform.' '.$version,
973
            'os_type' => php_uname(),
974
        ];
975
976
        return $data;
977
    }
978
979
    /**
980
     * @param array $params
981
     * @param EntityManager $em
982
     */
983
    protected function sendAppData($params, EntityManager $em)
984
    {
985
        try {
986
            $query = http_build_query($this->createAppData($params, $em));
987
            $header = [
988
                'Content-Type: application/x-www-form-urlencoded',
989
                'Content-Length: '.strlen($query),
990
            ];
991
            $context = stream_context_create(
992
                [
993
                    'http' => [
994
                        'method' => 'POST',
995
                        'header' => $header,
996
                        'content' => $query,
997
                    ],
998
                ]
999
            );
1000
            file_get_contents('https://www.ec-cube.net/mall/use_site.php', false, $context);
1001
        } catch (\Exception $e) {
1002
            // 送信に失敗してもインストールは継続できるようにする
1003
            log_error($e->getMessage());
1004
        }
1005
1006
        return $this;
1007
    }
1008
1009
    /**
1010
     * @param EntityManager $em
1011
     *
1012
     * @return string
1013
     */
1014
    public function getDatabaseVersion(EntityManager $em)
1015
    {
1016
        $rsm = new \Doctrine\ORM\Query\ResultSetMapping();
1017
        $rsm->addScalarResult('server_version', 'server_version');
1018
1019
        $platform = $em->getConnection()->getDatabasePlatform()->getName();
1020 View Code Duplication
        switch ($platform) {
1021
            case 'sqlite':
1022
                $sql = 'SELECT sqlite_version() AS server_version';
1023
                break;
1024
1025
            case 'mysql':
1026
                $sql = 'SELECT version() AS server_version';
1027
                break;
1028
1029
            case 'postgresql':
1030
            default:
1031
                $sql = 'SHOW server_version';
1032
        }
1033
1034
        $version = $em->createNativeQuery($sql, $rsm)
1035
            ->getSingleScalarResult();
1036
1037
        // postgresqlのバージョンが10.x以降の場合に、getSingleScalarResult()で取得される不要な文字列を除く処理
1038
        if ($platform === 'postgresql') {
1039
            preg_match('/\A([\d+\.]+)/', $version, $matches);
1040
            $version = $matches[1];
1041
        }
1042
1043
        return $version;
1044
    }
1045
1046
    /**
1047
     * @param string
1048
     *
1049
     * @return string
1050
     */
1051
    public function convertAdminAllowHosts($adminAllowHosts)
1052
    {
1053
        if (empty($adminAllowHosts)) {
1054
            return '[]';
1055
        }
1056
1057
        $adminAllowHosts = \json_encode(
1058
            \explode("\n", StringUtil::convertLineFeed($adminAllowHosts))
1059
        );
1060
1061
        return "'$adminAllowHosts'";
1062
    }
1063
1064
    /**
1065
     * @return bool
1066
     */
1067
    protected function isInstalled()
1068
    {
1069
        return self::DEFAULT_AUTH_MAGIC !== $this->getParameter('eccube_auth_magic');
1070
    }
1071
1072
    /**
1073
     * @return bool
1074
     */
1075
    protected function isInstallEnv()
1076
    {
1077
        $env = $this->getParameter('kernel.environment');
1078
1079
        if ($env === 'install' || $env === 'test') {
1080
            return true;
1081
        }
1082
1083
        return false;
1084
    }
1085
}
1086