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

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