src/Eccube/Controller/Install/InstallController.php line 116

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