Completed
Push — 4.0 ( 87d096...bcc1be )
by Kiyotaka
05:44 queued 11s
created

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

Severity

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 View Code Duplication
        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
        return [
234
            'noWritePermissions' => $noWritePermissions,
235
        ];
236
    }
237
238
    /**
239
     * サイトの設定.
240
     *
241
     * @Route("/install/step3", name="install_step3")
242
     * @Template("step3.twig")
243
     *
244
     * @param Request $request
245
     *
246
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
247
     *
248
     * @throws \Doctrine\DBAL\DBALException
249
     * @throws \Exception
250
     */
251
    public function step3(Request $request)
252
    {
253
        if (!$this->isInstallEnv()) {
254
            throw new NotFoundHttpException();
255
        }
256
257
        $sessionData = $this->getSessionData($this->session);
258
259
        // 再インストールの場合は環境変数から復旧
260
        if ($this->isInstalled()) {
261
            // ショップ名/メールアドレス
262
            $conn = $this->entityManager->getConnection();
263
            $stmt = $conn->query('SELECT shop_name, email01 FROM dtb_base_info WHERE id = 1;');
264
            $row = $stmt->fetch();
265
            $sessionData['shop_name'] = $row['shop_name'];
266
            $sessionData['email'] = $row['email01'];
267
268
            $databaseUrl = $this->getParameter('eccube_database_url');
269
            $sessionData = array_merge($sessionData, $this->extractDatabaseUrl($databaseUrl));
270
271
            // 管理画面ルーティング
272
            $sessionData['admin_dir'] = $this->getParameter('eccube_admin_route');
273
274
            // 管理画面許可IP
275
            $sessionData['admin_allow_hosts'] = implode($this->getParameter('eccube_admin_allow_hosts'));
276
277
            // 強制SSL
278
            $sessionData['admin_force_ssl'] = $this->getParameter('eccube_force_ssl');
279
280
            // メール
281
            $mailerUrl = $this->getParameter('eccube_mailer_url');
282
            $sessionData = array_merge($sessionData, $this->extractMailerUrl($mailerUrl));
283
        } else {
284
            // 初期値設定
285
            if (!isset($sessionData['admin_allow_hosts'])) {
286
                $sessionData['admin_allow_hosts'] = '';
287
            }
288
            if (!isset($sessionData['smtp_host'])) {
289
                $sessionData = array_merge($sessionData, $this->extractMailerUrl('smtp://localhost:25'));
290
            }
291
        }
292
293
        $form = $this->formFactory
294
            ->createBuilder(Step3Type::class)
295
            ->getForm();
296
297
        $form->setData($sessionData);
298
        $form->handleRequest($request);
299
300 View Code Duplication
        if ($form->isSubmitted() && $form->isValid()) {
301
            $this->setSessionData($this->session, $form->getData());
302
303
            return $this->redirectToRoute('install_step4');
304
        }
305
306
        return [
307
            'form' => $form->createView(),
308
            'request' => $request,
309
        ];
310
    }
311
312
    /**
313
     * データベースの設定.
314
     *
315
     * @Route("/install/step4", name="install_step4")
316
     * @Template("step4.twig")
317
     *
318
     * @param Request $request
319
     *
320
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
321
     *
322
     * @throws \Exception
323
     */
324
    public function step4(Request $request)
325
    {
326
        if (!$this->isInstallEnv()) {
327
            throw new NotFoundHttpException();
328
        }
329
330
        $sessionData = $this->getSessionData($this->session);
331
332
        if (empty($sessionData['database'])) {
333
            // 再インストールの場合は環境変数から復旧.
334
            if ($this->isInstalled()) {
335
                $databaseUrl = $this->getParameter('eccube_database_url');
336
                $sessionData = array_merge($sessionData, $this->extractDatabaseUrl($databaseUrl));
337
            }
338
        }
339
340
        $form = $this->formFactory
341
            ->createBuilder(Step4Type::class)
342
            ->getForm();
343
344
        $form->setData($sessionData);
345
        $form->handleRequest($request);
346
347
        if ($form->isSubmitted() && $form->isValid()) {
348
            $data = $form->getData();
349
            if ($data['database'] === 'pdo_sqlite') {
350
                $data['database_name'] = '/var/eccube.db';
351
            }
352
353
            $this->setSessionData($this->session, $data);
354
355
            return $this->redirectToRoute('install_step5');
356
        }
357
358
        return [
359
            'form' => $form->createView(),
360
        ];
361
    }
362
363
    /**
364
     * データベースの初期化.
365
     *
366
     * @Route("/install/step5", name="install_step5")
367
     * @Template("step5.twig")
368
     *
369
     * @param Request $request
370
     *
371
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
372
     *
373
     * @throws \Exception
374
     */
375
    public function step5(Request $request)
376
    {
377
        if (!$this->isInstallEnv()) {
378
            throw new NotFoundHttpException();
379
        }
380
381
        $form = $this->formFactory
382
            ->createBuilder(Step5Type::class)
383
            ->getForm();
384
385
        $sessionData = $this->getSessionData($this->session);
386
        $form->setData($sessionData);
387
        $form->handleRequest($request);
388
389
        if ($form->isSubmitted() && $form->isValid()) {
390
            $noUpdate = $form['no_update']->getData();
391
392
            $url = $this->createDatabaseUrl($sessionData);
393
394
            try {
395
                $conn = $this->createConnection(['url' => $url]);
396
                $em = $this->createEntityManager($conn);
397
                $migration = $this->createMigration($conn);
398
399
                if ($noUpdate) {
400
                    $this->update($conn, [
401
                        'auth_magic' => $sessionData['authmagic'],
402
                        'login_id' => $sessionData['login_id'],
403
                        'login_pass' => $sessionData['login_pass'],
404
                        'shop_name' => $sessionData['shop_name'],
405
                        'email' => $sessionData['email'],
406
                    ]);
407
                } else {
408
                    $this->dropTables($em);
409
                    $this->createTables($em);
410
                    $this->importCsv($em);
411
                    $this->migrate($migration);
412
                    $this->insert($conn, [
413
                        'auth_magic' => $sessionData['authmagic'],
414
                        'login_id' => $sessionData['login_id'],
415
                        'login_pass' => $sessionData['login_pass'],
416
                        'shop_name' => $sessionData['shop_name'],
417
                        'email' => $sessionData['email'],
418
                    ]);
419
                }
420
            } catch (\Exception $e) {
421
                log_error($e->getMessage());
422
                $this->addError($e->getMessage());
423
424
                return [
425
                    'form' => $form->createView(),
426
                ];
427
            }
428
429
            if (isset($sessionData['agree']) && $sessionData['agree']) {
430
                $host = $request->getSchemeAndHttpHost();
431
                $basePath = $request->getBasePath();
432
                $params = [
433
                    'http_url' => $host.$basePath,
434
                    'shop_name' => $sessionData['shop_name'],
435
                ];
436
                $this->sendAppData($params, $em);
437
            }
438
            $version = $this->getDatabaseVersion($em);
439
            $this->setSessionData($this->session, ['database_version' => $version]);
440
441
            return $this->redirectToRoute('install_complete');
442
        }
443
444
        return [
445
            'form' => $form->createView(),
446
        ];
447
    }
448
449
    /**
450
     * インストール完了
451
     *
452
     * @Route("/install/complete", name="install_complete")
453
     * @Template("complete.twig")
454
     */
455
    public function complete(Request $request)
456
    {
457
        if (!$this->isInstallEnv()) {
458
            throw new NotFoundHttpException();
459
        }
460
461
        $sessionData = $this->getSessionData($this->session);
462
        $databaseUrl = $this->createDatabaseUrl($sessionData);
463
        $mailerUrl = $this->createMailerUrl($sessionData);
464
        $forceSSL = isset($sessionData['admin_force_ssl']) ? (bool) $sessionData['admin_force_ssl'] : false;
465 View Code Duplication
        if ($forceSSL === false) {
466
            $forceSSL = 'false';
467
        } elseif ($forceSSL === true) {
468
            $forceSSL = 'true';
469
        }
470
        $env = file_get_contents(__DIR__.'/../../../../.env.dist');
471
        $replacement = [
472
            'APP_ENV' => 'prod',
473
            'APP_DEBUG' => '0',
474
            'DATABASE_URL' => $databaseUrl,
475
            'MAILER_URL' => $mailerUrl,
476
            'ECCUBE_AUTH_MAGIC' => $sessionData['authmagic'],
477
            'DATABASE_SERVER_VERSION' => isset($sessionData['database_version']) ? $sessionData['database_version'] : '3',
478
            'ECCUBE_ADMIN_ALLOW_HOSTS' => $this->convertAdminAllowHosts($sessionData['admin_allow_hosts']),
479
            'ECCUBE_FORCE_SSL' => $forceSSL,
480
            'ECCUBE_ADMIN_ROUTE' => isset($sessionData['admin_dir']) ? $sessionData['admin_dir'] : 'admin',
481
            'ECCUBE_COOKIE_PATH' => $request->getBasePath() ? $request->getBasePath() : '/',
482
            'ECCUBE_TEMPLATE_CODE' => 'default',
483
            'ECCUBE_LOCALE' => 'ja',
484
        ];
485
486
        $env = StringUtil::replaceOrAddEnv($env, $replacement);
487
488
        if ($this->getParameter('kernel.environment') === 'install') {
489
            file_put_contents(__DIR__.'/../../../../.env', $env);
490
        }
491
        $host = $request->getSchemeAndHttpHost();
492
        $basePath = $request->getBasePath();
493
        $adminUrl = $host.$basePath.'/'.$replacement['ECCUBE_ADMIN_ROUTE'];
494
495
        $this->removeSessionData($this->session);
496
497
        $this->cacheUtil->clearCache('prod');
498
499
        return [
500
            'admin_url' => $adminUrl,
501
        ];
502
    }
503
504
    protected function getSessionData(SessionInterface $session)
505
    {
506
        return $session->get('eccube.session.install', []);
507
    }
508
509
    protected function removeSessionData(SessionInterface $session)
510
    {
511
        $session->clear();
512
    }
513
514
    protected function setSessionData(SessionInterface $session, $data = [])
515
    {
516
        $data = array_replace_recursive($this->getSessionData($session), $data);
517
        $session->set('eccube.session.install', $data);
518
    }
519
520
    protected function checkModules()
0 ignored issues
show
checkModules uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
521
    {
522
        foreach ($this->requiredModules as $module) {
523
            if (!extension_loaded($module)) {
524
                $this->addDanger(trans('install.required_extension_disabled', ['%module%' => $module]), 'install');
525
            }
526
        }
527
        if (!extension_loaded('pdo_mysql') && !extension_loaded('pdo_pgsql')) {
528
            $this->addDanger(trans('install.required_database_extension_disabled'), 'install');
529
        }
530
        foreach ($this->recommendedModules as $module) {
531
            if (!extension_loaded($module)) {
532
                if ($module == 'mcrypt' && PHP_VERSION_ID >= 70100) {
533
                    //The mcrypt extension has been deprecated in PHP 7.1.x
534
                    //http://php.net/manual/en/migration71.deprecated.php
535
                    continue;
536
                }
537
                $this->addInfo(trans('install.recommend_extension_disabled', ['%module%' => $module]), 'install');
538
            }
539
        }
540
        if ('\\' === DIRECTORY_SEPARATOR) { // for Windows
541
            if (!extension_loaded('wincache')) {
542
                $this->addInfo(trans('install.recommend_extension_disabled', ['%module%' => 'wincache']), 'install');
543
            }
544
        } else {
545
            if (!extension_loaded('apc')) {
546
                $this->addInfo(trans('install.recommend_extension_disabled', ['%module%' => 'apc']), 'install');
547
            }
548
        }
549
        if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) {
550
            if (!function_exists('apache_get_modules')) {
551
                $this->addWarning(trans('install.mod_rewrite_unknown'), 'install');
552
            } elseif (!in_array('mod_rewrite', apache_get_modules())) {
553
                $this->addDanger(trans('install.mod_rewrite_disabled'), 'install');
554
            }
555
        } elseif (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false) {
556
            // iis
557
        } elseif (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) {
558
            // nginx
559
        }
560
    }
561
562
    protected function createConnection(array $params)
563
    {
564
        if (strpos($params['url'], 'mysql') !== false) {
565
            $params['charset'] = 'utf8';
566
            $params['defaultTableOptions'] = [
567
                'collate' => 'utf8_general_ci',
568
            ];
569
        }
570
571
        Type::overrideType('datetime', UTCDateTimeType::class);
572
        Type::overrideType('datetimetz', UTCDateTimeTzType::class);
573
574
        $conn = DriverManager::getConnection($params);
575
        $conn->ping();
576
577
        $platform = $conn->getDatabasePlatform();
578
        $platform->markDoctrineTypeCommented('datetime');
579
        $platform->markDoctrineTypeCommented('datetimetz');
580
581
        return $conn;
582
    }
583
584
    protected function createEntityManager(Connection $conn)
585
    {
586
        $paths = [
587
            $this->getParameter('kernel.project_dir').'/src/Eccube/Entity',
588
            $this->getParameter('kernel.project_dir').'/app/Customize/Entity',
589
        ];
590
        $config = Setup::createConfiguration(true);
591
        $driver = new AnnotationDriver(new CachedReader(new AnnotationReader(), new ArrayCache()), $paths);
592
        $driver->setTraitProxiesDirectory($this->getParameter('kernel.project_dir').'/app/proxy/entity');
593
        $config->setMetadataDriverImpl($driver);
594
595
        $em = EntityManager::create($conn, $config);
596
597
        return $em;
598
    }
599
600
    /**
601
     * @param array $params
602
     *
603
     * @return string
604
     */
605
    public function createDatabaseUrl(array $params)
606
    {
607
        if (!isset($params['database'])) {
608
            return null;
609
        }
610
611
        $url = '';
612
        switch ($params['database']) {
613
            case 'pdo_sqlite':
614
                $url = 'sqlite://'.$params['database_name'];
615
                break;
616
617
            case 'pdo_mysql':
618
            case 'pdo_pgsql':
619
                $url = str_replace('pdo_', '', $params['database']);
620
                $url .= '://';
621
                if (isset($params['database_user'])) {
622
                    $url .= $params['database_user'];
623
                    if (isset($params['database_password'])) {
624
                        $url .= ':'.\rawurlencode($params['database_password']);
625
                    }
626
                    $url .= '@';
627
                }
628 View Code Duplication
                if (isset($params['database_host'])) {
629
                    $url .= $params['database_host'];
630
                    if (isset($params['database_port'])) {
631
                        $url .= ':'.$params['database_port'];
632
                    }
633
                    $url .= '/';
634
                }
635
                $url .= $params['database_name'];
636
                break;
637
        }
638
639
        return $url;
640
    }
641
642
    /**
643
     * @param string $url
644
     *
645
     * @return array
646
     */
647
    public function extractDatabaseUrl($url)
648
    {
649
        if (preg_match('|^sqlite://(.*)$|', $url, $matches)) {
650
            return [
651
                'database' => 'pdo_sqlite',
652
                'database_name' => $matches[1],
653
            ];
654
        }
655
656
        $parsed = parse_url($url);
657
658
        if ($parsed === false) {
659
            throw new \Exception('Malformed parameter "url".');
660
        }
661
662
        return [
663
            'database' => 'pdo_'.$parsed['scheme'],
664
            'database_name' => ltrim($parsed['path'], '/'),
665
            'database_host' => $parsed['host'],
666
            'database_port' => isset($parsed['port']) ? $parsed['port'] : null,
667
            'database_user' => isset($parsed['user']) ? $parsed['user'] : null,
668
            'database_password' => isset($parsed['pass']) ? $parsed['pass'] : null,
669
        ];
670
    }
671
672
    /**
673
     * @param array $params
674
     *
675
     * @return string
676
     *
677
     * @see https://github.com/symfony/swiftmailer-bundle/blob/9728097df87e76e2db71fc41fd7d211c06daea3e/DependencyInjection/SwiftmailerTransportFactory.php#L80-L142
678
     */
679
    public function createMailerUrl(array $params)
680
    {
681
        $url = '';
682
        if (isset($params['transport'])) {
683
            $url = $params['transport'].'://';
684
        } else {
685
            $url = 'smtp://';
686
        }
687 View Code Duplication
        if (isset($params['smtp_username'])) {
688
            $url .= $params['smtp_username'];
689
            if (isset($params['smtp_password'])) {
690
                $url .= ':'.$params['smtp_password'];
691
            }
692
            $url .= '@';
693
        }
694
695
        $queryStrings = [];
696
        if (isset($params['encryption'])) {
697
            $queryStrings['encryption'] = $params['encryption'];
698
            if ($params['encryption'] === 'ssl' && !isset($params['smtp_port'])) {
699
                $params['smtp_port'] = 465;
700
            }
701
        }
702
        if (isset($params['auth_mode'])) {
703
            $queryStrings['auth_mode'] = $params['auth_mode'];
704
        } else {
705
            if (isset($params['smtp_username'])) {
706
                $queryStrings['auth_mode'] = 'plain';
707
            }
708
        }
709
        ksort($queryStrings, SORT_STRING);
710
711 View Code Duplication
        if (isset($params['smtp_host'])) {
712
            $url .= $params['smtp_host'];
713
            if (isset($params['smtp_port'])) {
714
                $url .= ':'.$params['smtp_port'];
715
            }
716
        }
717
718
        if (isset($params['smtp_username']) || array_values($queryStrings)) {
719
            $url .= '?';
720
            $i = count($queryStrings);
721
            foreach ($queryStrings as $key => $value) {
722
                $url .= $key.'='.$value;
723
                if ($i > 1) {
724
                    $url .= '&';
725
                }
726
                $i--;
727
            }
728
        }
729
730
        return $url;
731
    }
732
733
    /**
734
     * @param string $url
735
     *
736
     * @return array
737
     */
738
    public function extractMailerUrl($url)
739
    {
740
        $options = [
741
            'transport' => null,
742
            'smtp_username' => null,
743
            'smtp_password' => null,
744
            'smtp_host' => null,
745
            'smtp_port' => null,
746
            'encryption' => null,
747
            'auth_mode' => null,
748
        ];
749
750
        if ($url) {
751
            $parts = parse_url($url);
752
            if (isset($parts['scheme'])) {
753
                $options['transport'] = $parts['scheme'];
754
            }
755
            if (isset($parts['user'])) {
756
                $options['smtp_username'] = $parts['user'];
757
            }
758
            if (isset($parts['pass'])) {
759
                $options['smtp_password'] = $parts['pass'];
760
            }
761
            if (isset($parts['host'])) {
762
                $options['smtp_host'] = $parts['host'];
763
            }
764
            if (isset($parts['port'])) {
765
                $options['smtp_port'] = $parts['port'];
766
            }
767
            if (isset($parts['query'])) {
768
                parse_str($parts['query'], $query);
769
                foreach (array_keys($options) as $key) {
770
                    if (isset($query[$key]) && $query[$key] != '') {
771
                        $options[$key] = $query[$key];
772
                    }
773
                }
774
            }
775
        }
776
        if (!isset($options['transport'])) {
777
            $options['transport'] = 'smtp';
778
        } elseif ('gmail' === $options['transport']) {
779
            $options['encryption'] = 'ssl';
780
            $options['auth_mode'] = 'login';
781
            $options['smtp_host'] = 'smtp.gmail.com';
782
            $options['transport'] = 'smtp';
783
        }
784
        if (!isset($options['smtp_port'])) {
785
            $options['smtp_port'] = 'ssl' === $options['encryption'] ? 465 : 25;
786
        }
787
        if (isset($options['smtp_username']) && !isset($options['auth_mode'])) {
788
            $options['auth_mode'] = 'plain';
789
        }
790
        ksort($options, SORT_STRING);
791
792
        return $options;
793
    }
794
795
    protected function createMigration(Connection $conn)
796
    {
797
        $config = new Configuration($conn);
798
        $config->setMigrationsNamespace('DoctrineMigrations');
799
        $migrationDir = $this->getParameter('kernel.project_dir').'/src/Eccube/Resource/doctrine/migration';
800
        $config->setMigrationsDirectory($migrationDir);
801
        $config->registerMigrationsFromDirectory($migrationDir);
802
803
        $migration = new Migration($config);
804
        $migration->setNoMigrationException(true);
805
806
        return $migration;
807
    }
808
809
    protected function dropTables(EntityManager $em)
810
    {
811
        $metadatas = $em->getMetadataFactory()->getAllMetadata();
812
        $schemaTool = new SchemaTool($em);
813
        $schemaTool->dropSchema($metadatas);
814
        $em->getConnection()->executeQuery('DROP TABLE IF EXISTS doctrine_migration_versions');
815
    }
816
817
    protected function createTables(EntityManager $em)
818
    {
819
        $metadatas = $em->getMetadataFactory()->getAllMetadata();
820
        $schemaTool = new SchemaTool($em);
821
        $schemaTool->createSchema($metadatas);
822
    }
823
824
    protected function importCsv(EntityManager $em)
825
    {
826
        // for full locale code cases
827
        $locale = env('ECCUBE_LOCALE', 'ja_JP');
828
        $locale = str_replace('_', '-', $locale);
829
        $locales = \Locale::parseLocale($locale);
830
        $localeDir = is_null($locales) ? 'ja' : $locales['language'];
831
832
        $loader = new \Eccube\Doctrine\Common\CsvDataFixtures\Loader();
833
        $loader->loadFromDirectory($this->getParameter('kernel.project_dir').'/src/Eccube/Resource/doctrine/import_csv/'.$localeDir);
834
        $executer = new \Eccube\Doctrine\Common\CsvDataFixtures\Executor\DbalExecutor($em);
835
        $fixtures = $loader->getFixtures();
836
        $executer->execute($fixtures);
837
    }
838
839
    protected function insert(Connection $conn, array $data)
840
    {
841
        $conn->beginTransaction();
842
        try {
843
            $salt = StringUtil::random(32);
844
            $this->encoder->setAuthMagic($data['auth_magic']);
845
            $password = $this->encoder->encodePassword($data['login_pass'], $salt);
846
847
            $id = ('postgresql' === $conn->getDatabasePlatform()->getName())
848
                ? $conn->fetchColumn("select nextval('dtb_base_info_id_seq')")
849
                : null;
850
851
            $conn->insert('dtb_base_info', [
852
                'id' => $id,
853
                'shop_name' => $data['shop_name'],
854
                'email01' => $data['email'],
855
                'email02' => $data['email'],
856
                'email03' => $data['email'],
857
                'email04' => $data['email'],
858
                'update_date' => new \DateTime(),
859
                'discriminator_type' => 'baseinfo',
860
            ], [
861
                'update_date' => \Doctrine\DBAL\Types\Type::DATETIME,
862
            ]);
863
864
            $member_id = ('postgresql' === $conn->getDatabasePlatform()->getName())
865
                ? $conn->fetchColumn("select nextval('dtb_member_id_seq')")
866
                : null;
867
868
            $conn->insert('dtb_member', [
869
                'id' => $member_id,
870
                'login_id' => $data['login_id'],
871
                'password' => $password,
872
                'salt' => $salt,
873
                'work_id' => 1,
874
                'authority_id' => 0,
875
                'creator_id' => 1,
876
                'sort_no' => 1,
877
                'update_date' => new \DateTime(),
878
                'create_date' => new \DateTime(),
879
                'name' => trans('install.member_name'),
880
                'department' => $data['shop_name'],
881
                'discriminator_type' => 'member',
882
            ], [
883
                'update_date' => Type::DATETIME,
884
                'create_date' => Type::DATETIME,
885
            ]);
886
            $conn->commit();
887
        } catch (\Exception $e) {
888
            $conn->rollback();
889
            throw $e;
890
        }
891
    }
892
893
    protected function update(Connection $conn, array $data)
894
    {
895
        $conn->beginTransaction();
896
        try {
897
            $salt = StringUtil::random(32);
898
            $stmt = $conn->prepare('SELECT id FROM dtb_member WHERE login_id = :login_id;');
899
            $stmt->execute([':login_id' => $data['login_id']]);
900
            $row = $stmt->fetch();
901
            $this->encoder->setAuthMagic($data['auth_magic']);
902
            $password = $this->encoder->encodePassword($data['login_pass'], $salt);
903
            if ($row) {
904
                // 同一の管理者IDであればパスワードのみ更新
905
                $sth = $conn->prepare('UPDATE dtb_member set password = :password, salt = :salt, update_date = current_timestamp WHERE login_id = :login_id;');
906
                $sth->execute([
907
                    ':password' => $password,
908
                    ':salt' => $salt,
909
                    ':login_id' => $data['login_id'],
910
                ]);
911
            } else {
912
                // 新しい管理者IDが入力されたらinsert
913
                $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');");
914
                $sth->execute([
915
                    ':login_id' => $data['login_id'],
916
                    ':password' => $password,
917
                    ':salt' => $salt,
918
                ]);
919
            }
920
            $stmt = $conn->prepare('UPDATE dtb_base_info set
921
                shop_name = :shop_name,
922
                email01 = :admin_mail,
923
                email02 = :admin_mail,
924
                email03 = :admin_mail,
925
                email04 = :admin_mail,
926
                update_date = current_timestamp
927
            WHERE id = 1;');
928
            $stmt->execute([
929
                ':shop_name' => $data['shop_name'],
930
                ':admin_mail' => $data['email'],
931
            ]);
932
            $conn->commit();
933
        } catch (\Exception $e) {
934
            $conn->rollback();
935
            throw $e;
936
        }
937
    }
938
939
    public function migrate(Migration $migration)
940
    {
941
        try {
942
            // nullを渡すと最新バージョンまでマイグレートする
943
            $migration->migrate(null, false);
944
        } catch (MigrationException $e) {
945
        }
946
    }
947
948
    /**
949
     * @param array $params
950
     * @param EntityManager $em
951
     *
952
     * @return array
953
     */
954
    public function createAppData($params, EntityManager $em)
955
    {
956
        $platform = $em->getConnection()->getDatabasePlatform()->getName();
957
        $version = $this->getDatabaseVersion($em);
958
        $data = [
959
            'site_url' => $params['http_url'],
960
            'shop_name' => $params['shop_name'],
961
            'cube_ver' => Constant::VERSION,
962
            'php_ver' => phpversion(),
963
            'db_ver' => $platform.' '.$version,
964
            'os_type' => php_uname(),
965
        ];
966
967
        return $data;
968
    }
969
970
    /**
971
     * @param array $params
972
     * @param EntityManager $em
973
     */
974
    protected function sendAppData($params, EntityManager $em)
975
    {
976
        try {
977
            $query = http_build_query($this->createAppData($params, $em));
978
            $header = [
979
                'Content-Type: application/x-www-form-urlencoded',
980
                'Content-Length: '.strlen($query),
981
            ];
982
            $context = stream_context_create(
983
                [
984
                    'http' => [
985
                        'method' => 'POST',
986
                        'header' => $header,
987
                        'content' => $query,
988
                    ],
989
                ]
990
            );
991
            file_get_contents('https://www.ec-cube.net/mall/use_site.php', false, $context);
992
        } catch (\Exception $e) {
993
            // 送信に失敗してもインストールは継続できるようにする
994
            log_error($e->getMessage());
995
        }
996
997
        return $this;
998
    }
999
1000
    /**
1001
     * @param EntityManager $em
1002
     *
1003
     * @return string
1004
     */
1005
    public function getDatabaseVersion(EntityManager $em)
1006
    {
1007
        $rsm = new \Doctrine\ORM\Query\ResultSetMapping();
1008
        $rsm->addScalarResult('server_version', 'server_version');
1009
1010
        $platform = $em->getConnection()->getDatabasePlatform()->getName();
1011 View Code Duplication
        switch ($platform) {
1012
            case 'sqlite':
1013
                $sql = 'SELECT sqlite_version() AS server_version';
1014
                break;
1015
1016
            case 'mysql':
1017
                $sql = 'SELECT version() AS server_version';
1018
                break;
1019
1020
            case 'postgresql':
1021
            default:
1022
                $sql = 'SHOW server_version';
1023
        }
1024
1025
        $version = $em->createNativeQuery($sql, $rsm)
1026
            ->getSingleScalarResult();
1027
1028
        // postgresqlのバージョンが10.x以降の場合に、getSingleScalarResult()で取得される不要な文字列を除く処理
1029
        if ($platform === 'postgresql') {
1030
            preg_match('/\A([\d+\.]+)/', $version, $matches);
1031
            $version = $matches[1];
1032
        }
1033
1034
        return $version;
1035
    }
1036
1037
    /**
1038
     * @param string
1039
     *
1040
     * @return string
1041
     */
1042
    public function convertAdminAllowHosts($adminAllowHosts)
1043
    {
1044
        if (empty($adminAllowHosts)) {
1045
            return '[]';
1046
        }
1047
1048
        $adminAllowHosts = \json_encode(
1049
            \explode("\n", StringUtil::convertLineFeed($adminAllowHosts))
1050
        );
1051
1052
        return "'$adminAllowHosts'";
1053
    }
1054
1055
    /**
1056
     * @return bool
1057
     */
1058
    protected function isInstalled()
1059
    {
1060
        return self::DEFAULT_AUTH_MAGIC !== $this->getParameter('eccube_auth_magic');
1061
    }
1062
1063
    /**
1064
     * @return bool
1065
     */
1066
    protected function isInstallEnv()
1067
    {
1068
        $env = $this->getParameter('kernel.environment');
1069
1070
        if ($env === 'install' || $env === 'test') {
1071
            return true;
1072
        }
1073
1074
        return false;
1075
    }
1076
}
1077